using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Xml;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

using Athena.Core;

namespace Athena.Objects
{
    [Serializable()]
    public class BezierConnector : Connector
    {

        public enum ProcessDecorator
        {
            None,
            Empty,
            Omitted,
            Uncertain,
            Association,
            Dissociation
        }

        private ProcessDecorator _MiddleDecorator = ProcessDecorator.None;
        public ProcessDecorator MiddleDecorator
        {
            get { return _MiddleDecorator; }
            set { _MiddleDecorator = value; }
        }

        /// <summary>
        /// Clone this reaction. Inputs and outputs are NOT cloned
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            BezierConnector oResult = new BezierConnector(new List<IDrawable>(), Reactants.Count);
            oResult._numInputs = _numInputs;
            oResult.Location = Location;
            oResult.Dimensions = Dimensions;
            oResult.Text = Text;
            oResult.Item =(Item)_Item.Clone();
            oResult.Type = Type;
            oResult.ColorStart = ColorStart;
            oResult.MiddleDecorator = MiddleDecorator;
            
            oResult._MiddleSegment = (ControlPoint[])_MiddleSegment.Clone();
            oResult.CurveSegments.Clear();

            foreach (CurveSegment cs in _CurveSegments)
            {
                oResult.CurveSegments.Add( new CurveSegment(cs) );
            }

            oResult.FixMiddleSegment();
            oResult.DoneModifying(Corner.None,false);

            return oResult;
        }
        
        /// <summary>
        /// Generate an alias. All inputs and outputs are also aliased
        /// </summary>
        /// <returns></returns>
        public override IDrawable GenerateAlias()
        {
            BezierConnector oResult = new BezierConnector(new List<IDrawable>(), Reactants.Count);
            oResult._numInputs = _numInputs;
            oResult.Location = Location;
            oResult.Dimensions = Dimensions;
            oResult.Text = Text;
            oResult.Type = Type;
            oResult.ColorStart = ColorStart;
            oResult.MiddleDecorator = MiddleDecorator;

            oResult.CurveSegments.Clear();

            foreach (CurveSegment cs in _CurveSegments)
            {
                oResult.CurveSegments.Add(cs.GenerateAlias());
            }

            oResult.FixMiddleSegment();
            oResult.DoneModifying(Corner.None,false);

            if (_Item != null && _Item.Drawable.Alias != null && _Item.Drawable.Alias.Contains(this))
                _Item.Drawable.AddAlias(oResult);
            else
            {                
                AddAlias(oResult);
            }
            
            

            foreach (IDrawable id in Reactants)
            {
                if (id is IGlyph)
                {
                    ((IGlyph)id).AddIncomingConnection(oResult);
                }
            }
            foreach (IDrawable id in Products)
            {
                if (id is IGlyph)
                {
                    ((IGlyph)id).AddOutgoingConnection(oResult);
                }
                else
                    if (id is Connector)
                        ((Connector)id).Modifiers.Add(this);
            }
            

            return oResult;
        }
        /// <summary>
        /// Get all the drawables that are connected in one way or another to this drawable
        /// </summary>
        /*public override List<IDrawable> LinkedDrawables
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                list.AddRange(_LinkedDrawables);
                list.AddRange(InputsAndOutputs);
                return list;
            }
            set
            {
                base.LinkedDrawables = value;
            }
        }*/
        /// <summary>
        /// Draws a circle and a line from point 1 to point 2 using the given pen
        /// </summary>
        /// <param name="g"></param>
        /// <param name="pen"></param>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        public static void DrawLineAndCircle(Graphics g, Pen pen, PointF p1, PointF p2)
        {
            float slope = Math.Abs((p1.Y - p2.Y) / (p1.X - p2.X)),
                  angle = (float)Math.Atan(slope);

            if (p1.X > p2.X)   //all this work is to make sure that that line doesn't meet the circle!
            {
                if (p1.Y > p2.Y)
                {
                    g.DrawLine(pen, new PointF(p1.X - 2 * (float)Math.Cos(angle), p1.Y - 2 * (float)Math.Sin(angle)), p2);
                }
                else
                {
                    g.DrawLine(pen, new PointF(p1.X - 2 * (float)Math.Cos(angle), p1.Y + 2 * (float)Math.Sin(angle)), p2);
                }
            }
            else
            {
                if (p1.Y > p2.Y)
                {
                    g.DrawLine(pen, new PointF(p1.X + 2 * (float)Math.Cos(angle), p1.Y - 2 * (float)Math.Sin(angle)), p2);
                }
                else
                {
                    g.DrawLine(pen, new PointF(p1.X + 2 * (float)Math.Cos(angle), p1.Y + 2 * (float)Math.Sin(angle)), p2);
                }
            }
            g.DrawEllipse(pen, p1.X - 3F, p1.Y - 3F, 6F, 6F);  //draw the circle
        }

        /// <summary>
        /// Makes two CurveSegments that are identical to half of the given bezier (specified by the 4 points)
        /// </summary>
        /// <param name="node">The node that is connected to this curve</param>
        /// <returns>two CurveSegments</returns>
        public static CurveSegment[] GetHalfCurve(PointF start, PointF end, PointF control1, PointF control2, IDrawable node)
        {
            float t = 0.5f * 0.5f * 0.5f;
            PointF midpt = new PointF(
                t * (start.X + 3 * (control1.X + control2.X) + end.X),
                t * (start.Y + 3 * (control1.Y + control2.Y) + end.Y));

            t = 0.30f;
            float t1 = t * t * t, t2 = t * t * (1 - t), t3 = t * (1 - t) * (1 - t), t4 = (1 - t) * (1 - t) * (1 - t);
            PointF side1 = new PointF(
                t1 * start.X + 3 * (t2 * control1.X + t3 * control2.X) + t4 * end.X,
                t1 * start.Y + 3 * (t2 * control1.Y + t3 * control2.Y) + t4 * end.Y);

            PointF side2 = new PointF(
                t4 * start.X + 3 * (t3 * control1.X + t2 * control2.X) + t1 * end.X,
                t4 * start.Y + 3 * (t3 * control1.Y + t2 * control2.Y) + t1 * end.Y);

            CurveSegment[] halves = new CurveSegment[2];
            halves[0] = new CurveSegment(node, new ControlPoint(side1), new ControlPoint(side1), new ControlPoint(midpt));
            halves[1] = new CurveSegment(node, new ControlPoint(side2), new ControlPoint(side2), new ControlPoint(midpt));
            return halves;
        }

        /// <summary>
        /// This class is used to draw a series of beziers or lines from one point to another.
        /// It stores a list of control points and start/end points
        /// </summary>
        [Serializable()]
        public class CurveSegment
        {
            [Serializable()]
            public class Segment 
            {
                public ControlPoint Start, End, ControlPoint1, ControlPoint2;

                public override string ToString()
                {
                    return Start.ToString() + "," + ControlPoint1.ToString() + "," + ControlPoint2.ToString() + "," + End.ToString();
                }

                public Segment()
                {
                    Start = new ControlPoint();
                    End = new ControlPoint();
                    ControlPoint1 = new ControlPoint();
                    ControlPoint2 = new ControlPoint();
                }
                public Segment(PointF start, PointF ctrl1, PointF ctrl2, PointF end)
                {
                    Start = new ControlPoint(start);
                    End = new ControlPoint(end);
                    ControlPoint1 = new ControlPoint(ctrl1);
                    ControlPoint2 = new ControlPoint(ctrl2);
                }
                public Segment(ControlPoint start, ControlPoint ctrl1, ControlPoint ctrl2, ControlPoint end)
                {
                    Start = start;
                    End = end;
                    ControlPoint1 = ctrl1;
                    ControlPoint2 = ctrl2;
                }
                public Segment(Segment copy)
                {
                    Start = new ControlPoint(copy.Start);
                    End = new ControlPoint(copy.End);
                    ControlPoint1 = new ControlPoint(copy.ControlPoint1);
                    ControlPoint2 = new ControlPoint(copy.ControlPoint2);
                }
                /// <summary>
                /// write information about this segment to an XML file
                /// </summary>
                /// <param name="reader"></param>
                public void WriteXML(XmlWriter writer)
                {
                    writer.WriteStartElement("Segment", "");

                    writer.WriteStartElement("Start", "");
                        writer.WriteStartElement("X", "");
                        writer.WriteValue(Start.X);
                        writer.WriteEndElement();
                        writer.WriteStartElement("Y", "");
                        writer.WriteValue(Start.Y);
                        writer.WriteEndElement();
                    writer.WriteEndElement();

                    writer.WriteStartElement("ControlPoint1", "");
                        writer.WriteStartElement("X", "");
                        writer.WriteValue(ControlPoint1.X);
                        writer.WriteEndElement();
                        writer.WriteStartElement("Y", "");
                        writer.WriteValue(ControlPoint1.Y);
                        writer.WriteEndElement();
                    writer.WriteEndElement();

                    writer.WriteStartElement("ControlPoint2", "");
                        writer.WriteStartElement("X", "");
                        writer.WriteValue(ControlPoint2.X);
                        writer.WriteEndElement();
                        writer.WriteStartElement("Y", "");
                        writer.WriteValue(ControlPoint2.Y);
                        writer.WriteEndElement();
                    writer.WriteEndElement();

                    writer.WriteStartElement("End", "");
                        writer.WriteStartElement("X", "");
                        writer.WriteValue(End.X);
                        writer.WriteEndElement();
                        writer.WriteStartElement("Y", "");
                        writer.WriteValue(End.Y);
                        writer.WriteEndElement();
                    writer.WriteEndElement();

                    writer.WriteEndElement();
                }

                /// <summary>
                /// read information about this segment from an XML file
                /// </summary>
                /// <param name="reader"></param>
                public bool ReadXML(XmlReader reader)
                {
                    bool read = reader.Read();

                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "Segment")
                    {
                        float x = 0f, y = 0f;
                        string type = "";

                        read = reader.Read();
                        while (read)
                        {
                            if (reader.NodeType == XmlNodeType.EndElement)
                                break;

                            if (reader.NodeType == XmlNodeType.Element)  //start, controlpoint1, controlpoint2, end
                            {
                                type = reader.Name;
                                read = reader.Read();

                                while (read)    //x, y
                                {
                                    if (reader.NodeType == XmlNodeType.EndElement)
                                        break;

                                    if (reader.NodeType == XmlNodeType.Element)  //x or y
                                    {
                                        switch (reader.Name)
                                        {
                                            case "X":
                                                read = reader.Read();
                                                if (reader.NodeType == XmlNodeType.Text)
                                                    x = reader.ReadContentAsFloat();
                                                else
                                                    read = reader.Read();
                                                break;
                                            case "Y":
                                                read = reader.Read();
                                                if (reader.NodeType == XmlNodeType.Text)
                                                    y = reader.ReadContentAsFloat();
                                                else
                                                    read = reader.Read();
                                                break;
                                            default:
                                                while (reader.NodeType != XmlNodeType.EndElement)
                                                    read = reader.Read();
                                                break;
                                        }
                                    }

                                    if (!read) break;
                                    read = reader.Read();
                                }

                                switch (type)
                                {
                                    case "Start":
                                        Start.X = x;
                                        Start.Y = y;
                                        break;
                                    case "ControlPoint1":
                                        ControlPoint1.X = x;
                                        ControlPoint1.Y = y;
                                        break;
                                    case "ControlPoint2":
                                        ControlPoint2.X = x;
                                        ControlPoint2.Y = y;
                                        break;
                                    case "End":
                                        End.X = x;
                                        End.Y = y;
                                        break;
                                    default:
                                        break;
                                }
                            }

                            if (!read) break;

                            read = reader.Read();
                        }
                        return true;
                    }
                    return false;
                }
            }

            /// <summary>
            /// writes each segment into XML
            /// </summary>
            /// <param name="writer"></param>
            public void WriteXML(XmlWriter writer, Item edge, Hashtable AllItemsWithPrefix)
            {
                if (StartNode == null) return;

                writer.WriteStartElement("CurveSegment", "");

                writer.WriteStartElement("StartNode", "");

                if (edge != null && edge.Parent != null && StartNode.Item != null)
                {
                    writer.WriteString(edge.ParentModule.Rename(StartNode.Name, StartNode.Item.Parent, AllItemsWithPrefix, false));
                }
                else
                    writer.WriteString(StartNode.Name);
                                
                writer.WriteEndElement();

                writer.WriteStartElement("NodeLocation", "");
                writer.WriteStartElement("X", "");
                writer.WriteValue(StartNode.Location.X);
                writer.WriteEndElement();
                writer.WriteStartElement("Y", "");
                writer.WriteValue(StartNode.Location.Y);
                writer.WriteEndElement();
                writer.WriteEndElement();

                writer.WriteStartElement("IsStraight", "");
                writer.WriteString(IsStraight.ToString());
                writer.WriteEndElement();

                writer.WriteStartElement("IsModifier", "");
                writer.WriteString(IsModifier.ToString());
                writer.WriteEndElement();

                writer.WriteStartElement("IsReactant", "");
                writer.WriteString(IsReactant.ToString());
                writer.WriteEndElement();

                writer.WriteStartElement("ArrowHeadType", "");
                writer.WriteString(ArrowHead.ToString());
                writer.WriteEndElement();

                writer.WriteStartElement("ArrowHeadSize", "");
                writer.WriteValue(ArrowSize);
                writer.WriteEndElement();

                writer.WriteStartElement("DistanceFromNode", "");
                writer.WriteValue(Distance);
                writer.WriteEndElement();

                writer.WriteStartElement("Segments", "");
                foreach (Segment s in Segments)
                {
                    s.WriteXML(writer);
                }
                writer.WriteEndElement();

                writer.WriteEndElement();
            }

            /// <summary>
            /// reads each segment from XML
            /// </summary>
            /// <param name="writer"></param>
            public bool ReadXML(XmlReader reader, Connector connector, Module root, Hashtable AllItemsWithPrefix)
            {
                IDrawable parent = connector.Parent;

                bool read = reader.Read();
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "CurveSegment")
                {
                    if (Segments == null)
                        Segments = new List<Segment>();
                    else
                        Segments.Clear();
                    read = reader.Read();

                    while (read && reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "NodeLocation":
                                read = reader.Read();
                                float x = 0, y = 0;
                                while (read)    //x, y
                                {
                                    if (reader.NodeType == XmlNodeType.EndElement)
                                        break;

                                    if (reader.NodeType == XmlNodeType.Element)  //x or y
                                    {
                                        switch (reader.Name)
                                        {
                                            case "X":
                                                read = reader.Read();
                                                if (reader.NodeType == XmlNodeType.Text)
                                                    x = reader.ReadContentAsFloat();
                                                else
                                                    read = reader.Read();
                                                break;
                                            case "Y":
                                                read = reader.Read();
                                                if (reader.NodeType == XmlNodeType.Text)
                                                    y = reader.ReadContentAsFloat();
                                                else
                                                    read = reader.Read();
                                                break;
                                            default:
                                                while (reader.NodeType != XmlNodeType.EndElement)
                                                    read = reader.Read();
                                                break;
                                        }
                                    }

                                    if (!read) break;
                                    read = reader.Read();
                                }
                                if (StartNode != null)
                                {
                                    PointF point = new PointF(x,y);
                                    if (StartNode.Location != point && StartNode.Alias != null)
                                    {
                                        foreach (IDrawable alias in StartNode.Alias)
                                        {
                                            if (alias.Location == point &&
                                                ((parent == null && alias.Parent == null) || (parent != null && parent.IsParentOf(alias))))
                                            {
                                                if (StartNode is IGlyph) ((IGlyph)StartNode).RemoveConnector(connector);
                                                StartNode = alias;
                                                break;
                                            }
                                        }
                                    }
                                }
                                break;
                            case "StartNode":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string id = reader.ReadContentAsString();
                                    if (root != null && AllItemsWithPrefix != null)
                                    {
                                        Node node = root.FindItem(id, AllItemsWithPrefix) as Node;
                                        if (node != null && node.Drawable != null)
                                        {
                                            StartNode = node.Drawable;
                                        }
                                    }
                                    if (StartNode == null)
                                    {
                                        StartNode = Program.Model.FindItemWithID(id);
                                    }
                                }
                                else
                                    read = reader.Read();
                                break;
                            case "IsStraight":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    IsStraight = bool.Parse(reader.ReadContentAsString());
                                else
                                    read = reader.Read();
                                break;
                            case "IsModifier":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    IsModifier = bool.Parse(reader.ReadContentAsString());
                                else
                                    read = reader.Read();
                                break;
                            case "IsReactant":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    IsReactant = bool.Parse(reader.ReadContentAsString());
                                else
                                    read = reader.Read();
                                break;
                            case "Segments":
                                bool valid = true;
                                while (valid)
                                {
                                    Segment s = new Segment();
                                    valid = s.ReadXML(reader);
                                    if (valid)
                                        Segments.Add(s);
                                }
                                break;
                            case "ArrowHeadType":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string text = reader.ReadContentAsString();
                                    if (text == ArrowHeadType.Arrow.ToString())
                                        ArrowHead = ArrowHeadType.Arrow;
                                    else
                                        if (text == ArrowHeadType.Bar.ToString())
                                            ArrowHead = ArrowHeadType.Bar;
                                        else
                                            if (text == ArrowHeadType.Catalysis.ToString())
                                                ArrowHead = ArrowHeadType.Catalysis;
                                            else
                                                if (text == ArrowHeadType.Circle.ToString())
                                                    ArrowHead = ArrowHeadType.Circle;
                                                else
                                                    if (text == ArrowHeadType.Inhibition.ToString())
                                                        ArrowHead = ArrowHeadType.Inhibition;
                                                    else
                                                        if (text == ArrowHeadType.Modulation.ToString())
                                                            ArrowHead = ArrowHeadType.Modulation;
                                                        else
                                                            if (text == ArrowHeadType.Stimulation.ToString())
                                                                ArrowHead = ArrowHeadType.Stimulation;
                                                            else
                                                                if (text == ArrowHeadType.Trigger.ToString())
                                                                    ArrowHead = ArrowHeadType.Trigger;
                                                                else
                                                                    ArrowHead = ArrowHeadType.None;
                                }
                                else
                                    read = reader.Read();
                                break;
                            case "ArrowHeadSize":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    ArrowSize = reader.ReadContentAsFloat();
                                else
                                    read = reader.Read();
                                break;
                            case "DistanceFromNode":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    Distance = reader.ReadContentAsFloat();
                                else
                                    read = reader.Read();
                                break;
                            default:
                                read = reader.Read();
                                read = reader.Read();
                                break;
                        }
                        if (!read) break;
                        read = reader.Read();
                    }

                    if (StartNode is IGlyph)
                        if (IsReactant || IsModifier)
                            ((IGlyph)StartNode).AddOutgoingConnection(connector);
                        else
                            ((IGlyph)StartNode).AddIncomingConnection(connector);

                    return true;
                }
                else
                    return false;
            }

            public List<Segment> Segments = new List<Segment>();
            public PointF OldStart, StartPoint;   //this is used to check whether the object conencted to has moved
            public IDrawable StartNode;
            public float Coefficient, ArrowSize;
            public Connector.ArrowHeadType ArrowHead;
            public Color Color = Color.DeepSkyBlue;
            public bool IsReactant = false, IsModifier = false;

            public void Translate(PointF direction)
            {
                if (Segments == null) return;
                foreach (Segment s in Segments)
                {
                    s.ControlPoint1.X += direction.X;
                    s.ControlPoint1.Y += direction.Y;

                    s.ControlPoint2.X += direction.X;
                    s.ControlPoint2.Y += direction.Y;

                    s.Start.X += direction.X;
                    s.Start.Y += direction.Y;

                    s.End.X += direction.X;
                    s.End.Y += direction.Y;
                }
            }

            /// <summary>
            /// Copy the control point locations fron another curvesegments
            /// </summary>
            /// <param name="copy"></param>
            public void CopyPointsFrom(CurveSegment copy)
            {
                if (Segments == null)
                    Segments = new List<Segment>();
                else
                    Segments.Clear();

                if (copy.Segments == null) return;
                foreach (Segment s in copy.Segments)
                {
                    Segments.Add(new Segment(s));
                }
            }


            //public List<PointF[]> BezierPointsList;
            public PointF MinPoint, MaxPoint; //the minimum and maximum x,y 
            public bool IsFound, IsSelected, IsStraight, IsSquare;

            public int Count
            {
                get 
                {
                    if (Segments == null) return 0;
                    return Segments.Count; 
                }
            }
            /// <summary>
            /// empty curvesegment -- all values are null
            /// </summary>
            public CurveSegment() {}
            /// <summary>
            /// construct a new curve with just one subsegment
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="controlPt1"></param>
            /// <param name="controlPt2"></param>
            /// <param name="lastPt"></param>
            public CurveSegment(IDrawable obj, ControlPoint controlPt1, ControlPoint controlPt2, ControlPoint lastPt)
            {
                StartNode = obj;
                StartPoint = OldStart = StartNode.Location;
                Coefficient = 1;
                if (IsModifier)
                    ArrowHead = ArrowHeadType.Circle;
                else
                    ArrowHead = ArrowHeadType.Arrow;
                ArrowSize = 10;
                IsFound = IsSelected = IsStraight = IsSquare = false;

                Segments = new List<Segment>();

                Segments.Add(new Segment(new ControlPoint(StartPoint), controlPt1, controlPt2, lastPt));

                GeneratePoints();
            }
            /// <summary>
            /// Copy constructor
            /// </summary>
            /// <param name="copy"></param>
            public CurveSegment(CurveSegment copy)
            {
                StartNode = copy.StartNode;//.Clone() as IDrawable;
                StartPoint = OldStart = StartNode.Location;
                Coefficient = copy.Coefficient;
                ArrowHead = copy.ArrowHead;
                ArrowSize = copy.ArrowSize;
                IsFound = false;
                IsSelected = false;
                IsStraight = copy.IsStraight;
                IsSquare = copy.IsSquare;

                Segments = new List<Segment>(copy.Segments.Count);

                foreach (Segment s in copy.Segments)
                    Segments.Add(new Segment(s));                

                GeneratePoints();
            }
            /// <summary>
            /// Get an alias to this curvesegment with the start node aliased
            /// </summary>
            /// <returns></returns>
            public CurveSegment GenerateAlias()
            {
                CurveSegment copy = new CurveSegment();
                copy.StartNode = StartNode.GenerateAlias();
                StartPoint = OldStart = StartNode.Location;
                copy.Coefficient = Coefficient;
                copy.ArrowHead = ArrowHead;
                copy.ArrowSize = ArrowSize;
                copy.IsFound = false;
                copy.IsSelected = false;
                copy.IsStraight = IsStraight;
                copy.IsSquare = IsSquare;

                copy.Segments = new List<Segment>(Segments.Count);

                if (Segments != null)
                    foreach (Segment s in Segments)
                        copy.Segments.Add(new Segment(s));

                copy.GeneratePoints();
                return copy;
            }
            /// <summary>
            /// Generates points on the curve that can later be used to identify the curve
            /// </summary>
            public void GeneratePoints()
            {
                //BezierPointsList = new List<PointF[]>();
                if (Segments == null) Segments = new List<Segment>();
                if (StartNode == null) return;

                OldStart = StartNode.Location;
                ControlPoint startPt;
                
                if (IsReactant)
                    startPt = Start(_fDistance);
                else
                    startPt = Start(_fDistance + ArrowSize);

                StartPoint = startPt.Point;
                Segments[0].Start = startPt;
                MinPoint = MaxPoint = StartPoint;

                if (IsStraight)   //this will make the whole set of beziers into one line
                {
                    //this will allow the end point to hower around the node without pointing to the center

                    RectangleF bounds = StartNode.Bounds;
                    if (StartNode is BezierConnector)
                    {
                        bounds = ((BezierConnector)StartNode).MiddleRegion;
                    }

                    if (Segments[0].End.X > bounds.Left && Segments[0].End.X < bounds.Right)
                    {
                        Segments[0].ControlPoint1.X = StartPoint.X = Segments[0].End.X;
                    }
                    if (Segments[0].End.Y > bounds.Top && Segments[0].End.Y < bounds.Bottom)
                    {
                        Segments[0].ControlPoint1.Y = StartPoint.Y = Segments[0].End.Y;
                    }

                    ControlPoint endPt = Segments[0].End;

                    startPt = new ControlPoint(StartPoint);  //get the start of the curve
                    for (int i = 0; i < Segments.Count; ++i)
                    {
                        endPt = Segments[i].End;

                        float dx = Math.Abs(endPt.X - startPt.X), dy = Math.Abs(endPt.Y - startPt.Y);
                        /* make horizontal or vertical lines
                        float threshold = Math.Max(dx, dy) / 10f;
                        if (dx < threshold)
                            endPt.X = startPt.X;
                        else
                            if (dy < threshold)
                                endPt.Y = startPt.Y;
                        */
                        Segments[i].Start = startPt;
                        Segments[i].ControlPoint1 = new ControlPoint(AddTwoScaledPoints(startPt.Point, endPt.Point, 0.5F, 0.5F));
                        Segments[i].ControlPoint2 = new ControlPoint(Segments[i].ControlPoint1.Point);
                        startPt = Segments[i].End;
                    }                    
                }

                CleanUpDiagram(); //mainly to adjust the ends

                //main part:
                startPt = new ControlPoint(StartPoint);
                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    if (Segments[i].End == Segments[i].Start)   //remove such nonsense curves
                    {
                        if (Segments.Count > 1)
                            Segments.RemoveAt(i);
                        continue;
                    }
                    
                    //if (i < Segments.Count - 1) Segments[i + 1].Start = Segments[i].End; //maintain connectedness

                    int length = (int)PointDistance(Segments[i].End.Point, Segments[i].Start.Point) / 2;
                    PointF B;
                    float t = 0, dt = 1.0F / length;
                    for (int j = 0; j < length; ++j) //generate all the Bezier points for this bezier
                    {
                        B = new PointF(
                            Segments[i].Start.X * (1 - t) * (1 - t) * (1 - t) +
                              3 * Segments[i].ControlPoint1.X * (1 - t) * (1 - t) * t +
                              3 * Segments[i].ControlPoint2.X * (1 - t) * t * t +
                              Segments[i].End.X * t * t * t,

                              Segments[i].Start.Y * (1 - t) * (1 - t) * (1 - t) +
                              3 * Segments[i].ControlPoint1.Y * (1 - t) * (1 - t) * t +
                              3 * Segments[i].ControlPoint2.Y * (1 - t) * t * t +
                              Segments[i].End.Y * t * t * t
                              );

                        if (B.X < MinPoint.X) MinPoint.X = B.X;   //get the max and min points from all the generates points
                        if (B.Y < MinPoint.Y) MinPoint.Y = B.Y;
                        if (B.X > MaxPoint.X) MaxPoint.X = B.X;
                        if (B.Y > MaxPoint.Y) MaxPoint.Y = B.Y;

                        t += dt;
                    }
                    //BezierPointsList.Add(B); //add this bezier to the list                    
                }
            }

            /// <summary>
            /// Get the minimum distance from this segment to the given point
            /// </summary>
            /// <param name="oPoint"></param>
            /// <returns></returns>
            public double DistanceFrom(PointF oPoint)
            {
                double minDist = DistanceFrom(oPoint, 0);
                double temp;
                
                if (Segments == null) return 0;

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    temp = DistanceFrom(oPoint, i);
                    if (temp < minDist)
                        minDist = temp;
                }
                return minDist;
            }

            /// <summary>
            /// Get the closest point on this segment to the given point
            /// </summary>
            /// <param name="oPoint">point to compare</param>
            /// <returns></returns>
            public PointF ClosestPoint(PointF oPoint)
            {
                double minDist = DistanceFrom(oPoint, 0);
                PointF closest = oPoint;
                double temp;
                PointF p;


                if (Segments == null) return closest;

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    p = ClosestPointTo(oPoint, i);
                    temp = PointDistance(closest, oPoint);
                    if (temp < minDist)
                    {
                        minDist = temp;
                        closest = p;
                    }
                }
                return closest;
            }

            /// <summary>
            /// Inserts a new subsegment (new sub-bezier) at this location. 
            /// </summary>
            /// <param name="point"></param>
            public bool InsertNewSegment(PointF oPoint)
            {
                if (Segments == null || Segments.Count < 1) return false;

                int index = 0;
                double minDist = DistanceFrom(oPoint, 0);

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    if (DistanceFrom(oPoint,i) < minDist)
                    {
                        index = i;
                    }
                }
                if (index < 0) return false;

                ControlPoint oldEnd = Segments[index].End;  //save the old end-point

                //the new control points for the previous segment will fall on a straight line
                Segments[index].ControlPoint1 = new ControlPoint(AddTwoScaledPoints(Segments[index].Start.Point, oPoint, 0.5F, 0.5F));
                Segments[index].ControlPoint2 = new ControlPoint(AddTwoScaledPoints(Segments[index].Start.Point, oPoint, 0.5F, 0.5F));
                Segments[index].End = new ControlPoint(oPoint);  //this is where the previous segment will end and the new one will begin

                //the new segment to be added
                Segments.Insert(index+1,
                    new Segment(Segments[index].End,
                                new ControlPoint(AddTwoScaledPoints(oldEnd.Point, oPoint, 0.5F, 0.5F)),  //new control points line on a straight line
                                new ControlPoint(AddTwoScaledPoints(oldEnd.Point, oPoint, 0.5F, 0.5F)),  //same as the first control point (line)
                                oldEnd));   //the new end-point

                GeneratePoints();

                return true;
            }

            /// <summary>
            /// Finds the point that lies on the edge of the item that this curve is connected to
            /// </summary>
            /// <returns></returns>
            internal ControlPoint Start(float dist)
            {
                /*if (StartNode is Connector)
                {
                    return new ControlPoint(Utility.GetPointOnEdge((StartNode as Connector).MiddleRegion, Segments[0].ControlPoint1.Point, dist));
                }*/
                if (Segments == null || Segments.Count < 1)
                    if (StartNode != null)
                        return new ControlPoint(StartNode.Location);
                    else
                        return new ControlPoint();

                PointF startPt = Utility.GetPointOnEdge(StartNode.Bounds, Segments[0].ControlPoint1.Point, dist);

                return new ControlPoint(startPt);
            }

            /// <summary>
            /// Draws the set of curves from an item to the middle segment and the circle at the control point
            /// </summary>
            /// <param name="g"></param>
            /// <param name="pen"></param>
            public void Draw(Graphics g, Pen unselectedPen, Pen selectedPen, bool selected, bool drawArrow)
            {
                if (Segments == null || Segments.Count < 1 || StartNode == null) return;

                if (OldStart != StartNode.Location)
                    GeneratePoints();  //check is the StartNode obj has moved

                GraphicsPath oPath = new GraphicsPath();

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    if (IsStraight)
                    {
                        oPath.AddLine(Segments[i].Start.Point, Segments[i].End.Point);
                    }
                    else
                        oPath.AddBezier(Segments[i].Start.Point, Segments[i].ControlPoint1.Point, Segments[i].ControlPoint2.Point, Segments[i].End.Point);

                    if (selected)
                    {
                        if (!IsStraight)
                        {
                            DrawLineAndCircle(g, unselectedPen, Segments[i].ControlPoint1.Point, Segments[i].Start.Point);
                            if (i < Segments.Count - 1 || IsModifier)  //dont draw the last control point because it is the same as the middle segment's
                                DrawLineAndCircle(g, unselectedPen, Segments[i].ControlPoint2.Point, Segments[i].End.Point);
                        }
                        oPath.AddEllipse(Segments[i].End.X - 3F, Segments[i].End.Y - 3F, 6F, 6F);  //draw the circle end the ends
                    }
                }

                g.DrawPath(selectedPen, oPath);

                if (!drawArrow) return;

                //draw arrows:

                double slope, angle;

                /*if (StartNode is BezierConnector)   //modifier
                {
                    slope = ((BezierConnector)StartNode).MiddleSlope;
                    if (slope == 0)
                        angle = - Math.PI / 2;
                    else
                        angle = Math.Atan(- 1 / slope);                   
                }
                else
                {*/
                if (IsModifier)
                {
                    int last = Segments.Count - 1;
                    slope = Math.Abs((Segments[last].ControlPoint1.Y - Segments[last].End.Y) / (Segments[last].ControlPoint1.X - Segments[last].End.X));
                    angle = Math.Atan(slope);
                    if (Segments[last].ControlPoint1.X <= Segments[last].Start.X && Segments[last].ControlPoint1.Y <= Segments[last].Start.Y)
                        angle = Math.PI + angle;
                    else
                        if (Segments[last].ControlPoint1.X <= Segments[last].Start.X && Segments[last].ControlPoint1.Y >= Segments[last].Start.Y)
                            angle = Math.PI - angle;
                        else
                            if (Segments[last].ControlPoint1.X >= Segments[last].Start.X && Segments[last].ControlPoint1.Y <= Segments[last].Start.Y)
                                angle = 2 * Math.PI - angle;

                    DrawArrow(g, selectedPen, Segments[last].End.Point, angle, ArrowSize, ArrowHead);
                }
                else
                    if (!IsReactant)
                    {
                        slope = Math.Abs((Segments[0].ControlPoint1.Y - Segments[0].Start.Y) / (Segments[0].ControlPoint1.X - Segments[0].Start.X));
                        angle = Math.Atan(slope);
                        if (Segments[0].ControlPoint1.X <= Segments[0].Start.X && Segments[0].ControlPoint1.Y <= Segments[0].Start.Y)
                            angle = Math.PI + angle;
                        else
                            if (Segments[0].ControlPoint1.X <= Segments[0].Start.X && Segments[0].ControlPoint1.Y >= Segments[0].Start.Y)
                                angle = Math.PI - angle;
                            else
                                if (Segments[0].ControlPoint1.X >= Segments[0].Start.X && Segments[0].ControlPoint1.Y <= Segments[0].Start.Y)
                                    angle = 2 * Math.PI - angle;
                        //}
                        //System.Diagnostics.Debug.WriteLine(angle * 180/Math.PI);
                        //if (IsInput)
                        //    DrawArrow(g, selectedPen, StartPoint, -angle, ArrowSize, ArrowHead);
                        //else
                        DrawArrow(g, selectedPen, StartPoint, angle, ArrowSize, ArrowHead);
                    }                
                
            }


            /// <summary>
            /// Returns a list of the control points inside the given rect.             
            /// </summary>
            /// <param name="point"></param>
            /// <returns></returns>
            public List<ControlPoint> ControlPointsAt(RectangleF oRect)
            {
                List<ControlPoint> controlPointsList = new List<ControlPoint>();
                
                if (Segments == null || Segments.Count < 1) return controlPointsList;

                for (int i = 0; i < Segments.Count; ++i)
                {
                    if (oRect.Contains(Segments[i].ControlPoint1.Point) && !IsStraight)
                    {
                        if (!controlPointsList.Contains(Segments[i].ControlPoint1))
                            controlPointsList.Add(Segments[i].ControlPoint1);
                    }

                    if (oRect.Contains(Segments[i].ControlPoint2.Point) && !IsStraight)
                    {
                        if (!controlPointsList.Contains(Segments[i].ControlPoint2))
                            controlPointsList.Add(Segments[i].ControlPoint2);
                    }

                    if (oRect.Contains(Segments[i].End.Point))
                    {
                        if (!controlPointsList.Contains(Segments[i].End))
                            controlPointsList.Add(Segments[i].End);
                    }
                }

                return controlPointsList;
            }

            private float PercentCurvesToLeft()
            {
                if (!(StartNode is IGlyph)) return 0;

                List<Connector> connectors = ((IGlyph)StartNode).IncomingConnections;
                connectors.AddRange(((IGlyph)StartNode).OutgoingConnections);
                
                //mid-region of this curve's bezier
                float midPtX = Segments[ Segments.Count - 1 ].End.X;

                float count = 1;
                foreach (Connector c in connectors)   //count the number of beziers to the left of this one
                {
                    if (c.MiddleRegion.Right < midPtX) //mid-region of the other bezier
                        ++count; 
                }
                return count/(1 + (float)connectors.Count) - 0.5f;
            }

            float _fDistance = 5f;
            public float Distance
            {
                get { return _fDistance; }
                set { _fDistance = value; }
            }

            /// <summary>         
            /// makes sure that the curve's StartNode and control are not touching.
            /// </summary>            
            public void CleanUpDiagram()
            {
                //if (IsModifier && ArrowHead == ArrowHeadType.Arrow) ArrowHead = ArrowHeadType.Circle;
                if (Segments == null || Segments.Count < 1) return;

                if (StartNode is Connector) return;

                if (IsReactant || IsModifier)
                    StartPoint = Start(_fDistance).Point;
                else
                    StartPoint = Start(_fDistance + ArrowSize).Point;

                Segments[0].Start.Point = StartPoint;

                if (IsStraight)
                {
                    RectangleF bounds = StartNode.Bounds;
                    if (StartNode is BezierConnector && ((BezierConnector)StartNode).ShowMiddleSegment)
                    {
                        bounds = ((BezierConnector)StartNode).MiddleRegion;
                    }
                    if (Segments[0].End.X > bounds.Left && Segments[0].End.X < bounds.Right)
                    {
                        //float p = PercentCurvesToLeft();
                        Segments[0].ControlPoint1.X = Segments[0].Start.X = StartPoint.X = Segments[0].End.X;
                    }
                    if (Segments[0].End.Y > bounds.Top && Segments[0].End.Y < bounds.Bottom)
                    {
                        //float p = PercentCurvesToLeft();
                        Segments[0].ControlPoint1.Y = Segments[0].Start.Y = StartPoint.Y = Segments[0].End.Y;
                    }
                }

                PointF center = new PointF(StartNode.Location.X + StartNode.Dimensions.Width / 2, StartNode.Location.Y + StartNode.Dimensions.Height / 2),
                       startPt = StartPoint;

                float minLen = _fDistance + ArrowSize;
                PointF control1 = Segments[0].ControlPoint1.Point;
                RectangleF rect = StartNode.Bounds;
                rect.Inflate(minLen,minLen);

                //make sure that the arrow's control isn't too close or inside the item
                if (rect.Contains(Segments[0].ControlPoint1.Point))
                {
                    double angle = Math.Abs(Math.Atan((control1.Y - center.Y) / (control1.X - center.X)));


                    if (Segments[0].ControlPoint1.X > center.X)
                        control1.X = (float)(startPt.X + minLen * Math.Cos(angle));
                    else
                        control1.X = (float)(startPt.X - minLen * Math.Cos(angle));

                    if (Segments[0].ControlPoint1.Y > center.Y)
                        control1.Y = (float)(startPt.Y + minLen * Math.Sin(angle));
                    else
                        control1.Y = (float)(startPt.Y - minLen * Math.Sin(angle));

                    Segments[0].ControlPoint1 = new ControlPoint(control1);
                }
            }

            /// <summary>
            /// returns the minimum distance to this sub-curve
            /// </summary>
            /// <param name="point"></param>
            /// <param name="eps"></param>
            /// <param name="index"></param>
            /// <returns></returns>
            protected double DistanceFrom(PointF oPoint, int i)
            {
                double min = double.MaxValue, dist = 0;

                if (Segments == null || Segments.Count < 1) return min;

                PointF B = new PointF();
                int length = (int)PointDistance(Segments[i].End.Point, Segments[i].Start.Point) / 2;
                float t = 0, dt = 1.0F / length;
                for (int j = 0; j < length; ++j) //generate all the Bezier points for this bezier
                {
                    B = new PointF(
                        Segments[i].Start.X * (1 - t) * (1 - t) * (1 - t) +
                          3 * Segments[i].ControlPoint1.X * (1 - t) * (1 - t) * t +
                          3 * Segments[i].ControlPoint2.X * (1 - t) * t * t +
                          Segments[i].End.X * t * t * t ,

                          Segments[i].Start.Y * (1 - t) * (1 - t) * (1 - t) +
                          3 * Segments[i].ControlPoint1.Y * (1 - t) * (1 - t) * t +
                          3 * Segments[i].ControlPoint2.Y * (1 - t) * t * t +
                          Segments[i].End.Y * t * t * t
                          );

                    t += dt;

                    dist = PointDistance(B, oPoint);
                    if (dist < min)
                    {
                        min = dist;
                    }
                }

                return min;
            }

            /// <summary>
            /// returns the closest point to this sub-curve
            /// </summary>
            /// <param name="point">compare to</param>
            /// <param name="eps"></param>
            /// <param name="index"></param>
            /// <returns></returns>
            protected PointF ClosestPointTo(PointF oPoint, int i)
            {
                double min = double.MaxValue, dist = 0;
                PointF closest = oPoint;

                if (Segments == null || Segments.Count < 1) return closest;

                PointF B = new PointF();
                int length = (int)PointDistance(Segments[i].End.Point, Segments[i].Start.Point) / 2;
                float t = 0, dt = 1.0F / length;
                for (int j = 0; j < length; ++j) //generate all the Bezier points for this bezier
                {
                    B = new PointF(
                        Segments[i].Start.X * (1 - t) * (1 - t) * (1 - t) +
                          3 * Segments[i].ControlPoint1.X * (1 - t) * (1 - t) * t +
                          3 * Segments[i].ControlPoint2.X * (1 - t) * t * t +
                          Segments[i].End.X * t * t * t,

                          Segments[i].Start.Y * (1 - t) * (1 - t) * (1 - t) +
                          3 * Segments[i].ControlPoint1.Y * (1 - t) * (1 - t) * t +
                          3 * Segments[i].ControlPoint2.Y * (1 - t) * t * t +
                          Segments[i].End.Y * t * t * t
                          );

                    t += dt;

                    dist = PointDistance(B, oPoint);
                    if (dist < min)
                    {
                        min = dist;
                        closest = B;
                    }
                }

                return closest;
            }
            /// <summary>
            /// Returns true if the point lies near the curve or the control point
            /// </summary>
            /// <param name="point"></param>
            /// <returns></returns>
            public bool Contains(PointF oPoint)
            {
                if (StartNode == null) return false;
                return (Contains(oPoint, 8) > -1);
            }
            /// <summary>
            /// Checks if the point lies near (given epsilon) the curve or the control point
            /// Returns the index of the subsegment which contained the point
            /// </summary>
            /// <param name="oPoint"></param>
            /// <param name="eps"></param>
            /// <returns></returns>
            public int Contains(PointF oPoint, double eps)
            {
                if (StartNode == null) return -1;
                if (OldStart != StartNode.Location) GeneratePoints();

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    if (DistanceFrom(oPoint, i) < eps)
                    {
                        return i;
                    }
                }
                return -1;
            }
            /// <summary>
            /// Returns true if the rectangle meets any point on the curve
            /// </summary>
            /// <param name="point"></param>
            /// <returns></returns>
            public bool Contains(RectangleF rect)
            {
                if (StartNode == null) return false;
                if (OldStart != StartNode.Location) GeneratePoints();

                for (int i = 0; i < Segments.Count; ++i) //generates a bezier of each item in the list
                {
                    PointF B = new PointF();
                    int length = (int)PointDistance(Segments[i].End.Point, Segments[i].Start.Point) / 2;
                    float t = 0, dt = 1.0F / length;
                    for (int j = 0; j < length; ++j) //generate all the Bezier points for this bezier
                    {
                        B = new PointF(
                            Segments[i].Start.X * (1 - t) * (1 - t) * (1 - t) +
                              3 * Segments[i].ControlPoint1.X * (1 - t) * (1 - t) * t +
                              3 * Segments[i].ControlPoint2.X * (1 - t) * t * t +
                              Segments[i].End.X * t * t * t,

                              Segments[i].Start.Y * (1 - t) * (1 - t) * (1 - t) +
                              3 * Segments[i].ControlPoint1.Y * (1 - t) * (1 - t) * t +
                              3 * Segments[i].ControlPoint2.Y * (1 - t) * t * t +
                              Segments[i].End.Y * t * t * t
                              );

                        t += dt;

                        if (rect.Contains(B))
                        {
                            return true;  //a hit
                        }
                    }
                }
                return false;
            }

        }

        /// <summary>
        /// Find the CurveSegment connected to a particular node
        /// </summary>
        /// <param name="node">IDrawable</param>
        /// <returns></returns>
        public List<CurveSegment> FindCurveSegments(IDrawable node)
        {
            List<CurveSegment> list = new List<CurveSegment>();
            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.StartNode == node) list.Add(c);
            }
            return list;
        }

        /// <summary>
        /// Find the CurveSegment connected to a particular node with the given ID
        /// </summary>
        /// <param name="nodename">ID of node</param>
        /// <returns></returns>
        public List<CurveSegment> FindCurveSegments(string nodename)
        {
            List<CurveSegment> list = new List<CurveSegment>();
            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.StartNode.Name == nodename) list.Add(c);
            }
            return list;
        }

        /// <summary>
        /// Find the Substrate CurveSegment connected to a particular node with the given ID
        /// </summary>
        /// <param name="nodename">ID of node</param>
        /// <returns></returns>
        public List<CurveSegment> FindInputCurveSegments(string nodename)
        {
            List<CurveSegment> list = new List<CurveSegment>();
            int index = 0;
            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.StartNode.Name == nodename) list.Add(c);
                if (++index >= _numInputs) break;                
            }
            return list;
        }

        /// <summary>
        /// Find the Product CurveSegment connected to a particular node with the given ID
        /// </summary>
        /// <param name="nodename">ID of node</param>
        /// <returns></returns>
        public List<CurveSegment> FindOutputCurveSegments(string nodename)
        {
            List<CurveSegment> list = new List<CurveSegment>();
            int index = 0;
            foreach (CurveSegment c in _CurveSegments)
            {
                if (index >= _numInputs && c.StartNode.Name == nodename) list.Add(c);
                ++index;
            }
            return list;
        }

        /// <summary>
        /// A list of curves that is used to draw connectors from the objects to the center piece
        /// Each curve's StartNode is the object and end is the center piece
        /// </summary>
        protected List<CurveSegment> _CurveSegments = new List<CurveSegment>(); //the curves for the input and output - the main data of this class

        /// <summary>
        /// Returns all the curve segments in this bezier connector
        /// </summary>
        public List<CurveSegment> CurveSegments
        {
            get { return _CurveSegments; }
        }

        /// <summary>
        /// returns only the input (reactants) curve segments
        /// </summary>
        public List<CurveSegment> ReactantCurveSegments
        {
            get
            {
                List<CurveSegment> curves = new List<CurveSegment>();
                int i = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    curves.Add(c);
                    if (++i >= _numInputs) break;
                }
                return curves;
            }
        }

        /// <summary>
        /// returns only the output (products) curve segments
        /// </summary>
        public List<CurveSegment> ProductCurveSegments
        {
            get
            {
                List<CurveSegment> curves = new List<CurveSegment>();
                int i = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (i >= _numInputs)
                        curves.Add(c);
                    ++i;
                }
                return curves;
            }
        }

        /// <summary>
        /// returns only the input (reactants) curve segments
        /// </summary>
        public List<CurveSegment> ModifierCurveSegments
        {
            get
            {
                List<CurveSegment> curves = new List<CurveSegment>();
                
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsModifier)
                        curves.Add(c);                    
                }
                return curves;
            }
        }

        /// <summary>
        /// Four points determine the middle piece
        /// </summary>
        private ControlPoint[] _MiddleSegment = new ControlPoint[4];

        /// <summary>
        /// The middle segment is composed of 4 points: the first two determine the start and end
        /// of the line, and the second two determine the direction of the slopes at those points. 
        /// The first and second (and third and fourth) are always on the side of the 
        /// reactants and products, respectively. 
        /// </summary>
        public virtual ControlPoint[] MiddleSegment
        {
            get
            {
                return _MiddleSegment;
            }
            set
            {
                if (value != null && value.Length == 4 && !Immobile)
                {
                    _MiddleSegment = value;
                    FixMiddleSegment();
                }
            }
        }

        /// <summary>
        /// Performs the transformation while maintaining same x or y location with nearest 
        /// control point
        /// </summary>
        /// <param name="oDirection"></param>
        /// <param name="oPoint"></param>
        private void TransformPointWithControl(ControlPoint oPoint)
        {
            for (int j=0; j < _CurveSegments.Count; ++j)
            {
                CurveSegment c = _CurveSegments[j];

                for (int i=0; i < c.Segments.Count; ++i)
                {
                    CurveSegment.Segment s = c.Segments[i];
                    if (oPoint == s.Start)
                    {
                        if (i > 0)
                        {
                            if (Math.Abs(c.Segments[i - 1].Start.X - oPoint.X) < Math.Abs(c.Segments[i - 1].Start.Y - oPoint.Y))
                                oPoint.X = c.Segments[i - 1].Start.X;
                            else
                                oPoint.Y = c.Segments[i - 1].Start.Y;

                            if (Math.Abs(c.Segments[i].End.X - oPoint.X) < Math.Abs(c.Segments[i].End.Y - oPoint.Y))
                                oPoint.X = c.Segments[i].End.X;
                            else
                                oPoint.Y = c.Segments[i].End.Y;
                        }
                        else
                        {
                            if (Math.Abs(c.Segments[i].End.X - oPoint.X) < Math.Abs(c.Segments[i].End.Y - oPoint.Y))
                                oPoint.X = c.Segments[i].End.X;
                            else
                                oPoint.Y = c.Segments[i].End.Y;
                        }
                    }
                    else
                        if (oPoint == s.End)
                        {
                            if (i > 0)
                            {
                                if (Math.Abs(c.Segments[i - 1].End.X - oPoint.X) < Math.Abs(c.Segments[i - 1].End.Y - oPoint.Y))
                                    oPoint.X = c.Segments[i - 1].End.X;
                                else
                                    oPoint.Y = c.Segments[i - 1].End.Y;
                                if (Math.Abs(c.Segments[i].Start.X - oPoint.X) < Math.Abs(c.Segments[i].Start.Y - oPoint.Y))
                                    oPoint.X = c.Segments[i].Start.X;
                                else
                                    oPoint.Y = c.Segments[i].Start.Y;
                            }
                            else
                            {
                                if (Math.Abs(c.Segments[i].Start.X - oPoint.X) < Math.Abs(c.Segments[i].Start.Y - oPoint.Y))
                                    oPoint.X = c.Segments[i].Start.X;
                                else
                                    oPoint.Y = c.Segments[i].Start.Y;
                            }
                        }
                }
            }
        }

        /// <summary>
        /// Transforms all the selected control points on this connector by adding the given direction to each
        /// If control points are linked, those points are pasted together. 
        /// </summary>
        /// <param name="oDirection"></param>
        /// <param name="oCorner"></param>
        public override void Transform(PointF oDirection, ref Corner oCorner, bool Ctrl)
        {
            if (oCorner != Corner.ControlPoint || _CurveSegments == null || _CurveSegments.Count < 2 || 
                _MiddleSegment == null || _SelectedControlPoints == null || _SelectedControlPoints.Count < 1) return;

            if (_linkControlPoints)
            {
                for (int i = 1; i < _SelectedControlPoints.Count; ++i)
                    _SelectedControlPoints[i] = _SelectedControlPoints[0];
            }

            for (int i = 0; i < 1 && i < _SelectedControlPoints.Count; ++i)
            {
                _SelectedControlPoints[i].Add(oDirection);
                if (Ctrl)
                    TransformPointWithControl(_SelectedControlPoints[i]);
                

                //if it was the middle segment, then other parts of the middle segment have to be modified:

                if (_SelectedControlPoints[i] == _MiddleSegment[0] || _SelectedControlPoints[i] == _MiddleSegment[1] || _SelectedControlPoints[i] == _MiddleSegment[2] || _SelectedControlPoints[i] == _MiddleSegment[3])
                    if (_showMiddleSegment)    //there is a middle segment
                    {
                        if (Math.Abs(_MiddleSegment[0].X - _MiddleSegment[1].X) < Math.Abs(_MiddleSegment[0].Y - _MiddleSegment[1].Y)) //vertical
                        {
                            if (_MiddleSegment[0] == _SelectedControlPoints[i])
                                _MiddleSegment[1].X = _MiddleSegment[0].X;
                            else
                                if (_MiddleSegment[1] == _SelectedControlPoints[i])
                                    _MiddleSegment[0].X = _MiddleSegment[1].X;

                            _MiddleSegment[2].X = _MiddleSegment[3].X = _MiddleSegment[0].X;
                        }

                        else           //horizontal
                        {
                            if (_MiddleSegment[0] == _SelectedControlPoints[i])
                                _MiddleSegment[1].Y = _MiddleSegment[0].Y;
                            else
                                if (_MiddleSegment[1] == _SelectedControlPoints[i])
                                    _MiddleSegment[0].Y = _MiddleSegment[1].Y;

                            _MiddleSegment[2].Y = _MiddleSegment[3].Y = _MiddleSegment[0].Y;
                        }
                    }
                    else    //there is no middle segment
                    {
                        if (_SelectedControlPoints[i] == _MiddleSegment[0] || _SelectedControlPoints[i] == _MiddleSegment[1])   //the center point in selected
                        {
                            for (int j = 2; j < 4; ++j)    //move the other two control points too                            
                                _MiddleSegment[j].Add(oDirection);
                        }
                        else
                            if (_SelectedControlPoints[i] == _MiddleSegment[2])
                                _MiddleSegment[3].AddScaled(oDirection, 1, -1);
                            else
                                _MiddleSegment[2].AddScaled(oDirection, 1, -1);
                    }
            }//end for loop through all selected points

            /*foreach (CurveSegment c in _CurveSegments)
                if (c.IsSelected)
                    c.CleanUpDiagram();*/
        }

        /// <summary>
        /// Adds all control points inside the rect to the list SelectedControlPoints
        /// Returns none if that list is empty
        /// </summary>
        /// <param name="oRect"></param>
        /// <returns></returns>
        public override Corner Where(RectangleF oRect)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return Corner.None;

            _SelectedControlPoints.Clear();
            _SelectedControlPoints = SelectControlPointsAt(oRect);

            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.Contains(oRect))
                {
                    c.IsFound = true;
                }
            }

            if (_SelectedControlPoints.Count > 0)
                return Corner.ControlPoint;
            else
                return Corner.None;
        }

        /// <summary>
        /// Selects all segments in this rect
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public virtual void SelectSegmentsAt(RectangleF rect)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            //the following dirty code checks whether the rect meets the line
            bool midSegmentSelected = false;
            if (_MiddleSegment[0].Y == _MiddleSegment[1].Y)  //horizontal
            {
                midSegmentSelected =
                        (!(rect.Location.X > Math.Max(_MiddleSegment[0].X, _MiddleSegment[1].X) ||
                          (rect.Location.X + rect.Width) < Math.Min(_MiddleSegment[0].X, _MiddleSegment[1].X))
                        &&
                          _MiddleSegment[0].Y > rect.Location.Y && _MiddleSegment[0].Y < (rect.Location.Y + rect.Height));

            }
            else                            //vertical 
            {
                midSegmentSelected =
                       (!(rect.Location.Y > Math.Max(_MiddleSegment[0].Y, _MiddleSegment[1].Y) ||
                          (rect.Location.Y + rect.Height) < Math.Min(_MiddleSegment[0].Y, _MiddleSegment[1].Y))
                        &&
                          _MiddleSegment[0].X > rect.Location.X && _MiddleSegment[0].X < (rect.Location.X + rect.Width));
            }

            foreach (CurveSegment c in _CurveSegments)
                if (c.Contains(rect) || midSegmentSelected)
                {
                    c.IsFound = true;
                }
                else
                {
                    c.IsFound = false;
                }
        }
        /// <summary>
        /// Checks whether the given point is near the curve
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        public virtual void SelectSegmentsAt(PointF location)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;

            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.Contains(location))
                {
                    c.IsFound = true;
                }
                else
                {
                    c.IsFound = false;
                }
            }

            //then check the mid segment
            bool midSegmentselected = false;
            if (_MiddleSegment[0].Y == _MiddleSegment[1].Y)  //horizontal
            {
                if (_MiddleSegment[0].X < _MiddleSegment[1].X)
                    midSegmentselected = (location.X >= _MiddleSegment[0].X && location.X <= _MiddleSegment[1].X && Math.Abs(location.Y - _MiddleSegment[0].Y) < 6);
                else
                    midSegmentselected = (location.X >= _MiddleSegment[1].X && location.X <= _MiddleSegment[0].X && Math.Abs(location.Y - _MiddleSegment[0].Y) < 6);
            }
            else                            //vertical 
            {
                if (_MiddleSegment[0].Y < _MiddleSegment[1].Y)
                    midSegmentselected = (location.Y >= _MiddleSegment[0].Y && location.Y <= _MiddleSegment[1].Y && Math.Abs(location.X - _MiddleSegment[0].X) < 6);
                else
                    midSegmentselected = (location.Y >= _MiddleSegment[1].Y && location.Y <= _MiddleSegment[0].Y && Math.Abs(location.X - _MiddleSegment[0].X) < 6);
            }
            if (midSegmentselected)
            {
                foreach (CurveSegment c in _CurveSegments)
                    c.IsFound = true;
            }
        }

        /// <summary>
        /// Adds all control points near the point to the list SelectedControlPoints
        /// Returns none is no points were selected
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public override Corner Where(PointF oPoint)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return Corner.None;
            _SelectedControlPoints.Clear();
            
            if (_insertLineBreaks)
            {
                InsertLineBreak(oPoint);
                return Corner.None;
            }

            ControlPoint point = SelectControlPointsAt(oPoint);

            if (point != null)
            {
                if (!_SelectedControlPoints.Contains(point))
                    _SelectedControlPoints.Add(point);
                return Corner.ControlPoint;
            }
            else
                return Corner.None;
        }

        /// <summary>
        /// Returns a list of control points that lie in this rect
        /// </summary>
        /// <param name="oRect"></param>
        /// <returns></returns>
        public override List<ControlPoint> SelectControlPointsAt(RectangleF oRect)
        {
            List<ControlPoint> SelectedControlPoints = new List<ControlPoint>();
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return SelectedControlPoints;
            
            for (int i = 0; i < _MiddleSegment.Length; ++i)
            {
                if (oRect.Contains(_MiddleSegment[i].Point))
                {
                    if (!SelectedControlPoints.Contains(_MiddleSegment[i]))
                        SelectedControlPoints.Add(_MiddleSegment[i]);
                    foreach (CurveSegment c in _CurveSegments) 
                        c.IsFound = true;
                }
            }
            foreach (CurveSegment c in _CurveSegments)
            {
                foreach (ControlPoint p in c.ControlPointsAt(oRect))             //if one of the control points is hit                
                    if (!SelectedControlPoints.Contains(p))
                        SelectedControlPoints.Add(p);
            }

            return SelectedControlPoints;
        }

        /// <summary>
        /// returns a list of currently selected control points (usually a result of Where)
        /// </summary>
        public override List<ControlPoint> SelectedControlPoints
        {
            get
            {
                return _SelectedControlPoints;
            }
        }

        /// <summary>
        /// Selects all control points. To be used in conjunction with SelectedControlPoints
        /// </summary>
        /// <param name="oRect"></param>
        /// <returns></returns>
        public override void SelectAllControlPoints()
        {
            _SelectedControlPoints.Clear();
            foreach (CurveSegment c in _CurveSegments)
            {
                foreach (CurveSegment.Segment s in c.Segments)
                {
                    if (!_SelectedControlPoints.Contains(s.Start))
                        _SelectedControlPoints.Add(s.Start);
                    if (!_SelectedControlPoints.Contains(s.ControlPoint1))
                        _SelectedControlPoints.Add(s.ControlPoint1);
                    if (!_SelectedControlPoints.Contains(s.ControlPoint2))
                        _SelectedControlPoints.Add(s.ControlPoint2);
                    if (!_SelectedControlPoints.Contains(s.End))
                        _SelectedControlPoints.Add(s.End);
                }
            }
        }

        /// <summary>
        /// returns the topmost control point near this point
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public override ControlPoint SelectControlPointsAt(PointF oPoint)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return new ControlPoint(0, 0);
            RectangleF rect = new RectangleF(oPoint.X - 4F, oPoint.Y - 4F, 8F, 8F);

            for (int i = 0; i < _MiddleSegment.Length; ++i)    //mid segments control point is hit
            {
                if (rect.Contains(_MiddleSegment[i].Point))
                {
                    if (!_SelectedControlPoints.Contains(_MiddleSegment[i]))
                        _SelectedControlPoints.Add(_MiddleSegment[i]);

                    foreach (CurveSegment c in _CurveSegments) 
                        c.IsFound = true;

                    return _MiddleSegment[i];
                }
            }

            foreach (CurveSegment c in _CurveSegments)
            {
                List<ControlPoint> p = c.ControlPointsAt(rect);             //if one of the control points is hit                
                if (p != null && p.Count > 0)
                {
                    if (!_SelectedControlPoints.Contains(p[0]))
                        _SelectedControlPoints.Add(p[0]);  //add the topmose

                    c.IsFound = true;
                    return p[0];
                }

            }

            return null;
        }

        /// <summary>
        /// Checks all control points. If any pair are close to one another, they are made to point to the same.
        /// </summary>
        protected virtual void LinkOverlappingControlPoints()
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            ControlPoint point;
            foreach (CurveSegment c in _CurveSegments)
            {
                for (int i = 0; i < c.Segments.Count; ++i)
                {
                    point = SelectControlPointsAt(c.Segments[i].ControlPoint1.Point);
                    if (point != null)
                        c.Segments[i].ControlPoint1 = point;
                }
                for (int i = 0; i < c.Segments.Count; ++i)
                {
                    point = SelectControlPointsAt(c.Segments[i].ControlPoint2.Point);
                    if (point != null)
                        c.Segments[i].ControlPoint2 = point;
                }
                for (int i = 0; i < c.Segments.Count; ++i)
                {
                    point = SelectControlPointsAt(c.Segments[i].End.Point);
                    if (point != null)
                        c.Segments[i].End = point;
                }
            }
            for (int i = 0; i < _CurveSegments.Count; ++i)
            {
                CurveSegment curve = _CurveSegments[i];
                if (!curve.IsModifier)
                {
                    if (i < _numInputs)
                    {
                        curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                        curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[0];
                    }
                    else
                    {
                        curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                        curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[1];
                    }
                }
            }
        }

        /// <summary>
        /// Recalculates all the bezier points, since they have probably changed
        /// Clears temporary lists used to identify which control points were selected 
        /// </summary>
        public override void DoneModifying(Corner oCorner, bool modifyAnchoredItems)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;

            /*//alighn points that are close -- problematic, so off
            if (StraightLines)
            {
                SelectAllControlPoints();
                SizeF dim = Dimensions;
                float threshold = Math.Max(dim.Height, dim.Width) / 20.0f;

                foreach (ControlPoint c1 in _SelectedControlPoints)
                {
                    foreach (ControlPoint c2 in _SelectedControlPoints)
                    {
                        if (_MiddleSegment != null && _MiddleSegment.Length == 4 &&
                            (c2 == _MiddleSegment[0] || c2 == _MiddleSegment[1]))
                        {
                            if (Math.Abs(c1.X - c2.X) < threshold)
                            {
                                c1.X = c2.X;
                            }
                            if (Math.Abs(c1.Y - c2.Y) < threshold)
                            {
                                c1.Y = c2.Y;
                            }
                        }
                        else
                        {
                            if (Math.Abs(c1.X - c2.X) < threshold)
                            {
                                c1.X = c2.X = (c1.X + c2.X) / 2;
                            }
                            if (Math.Abs(c1.Y - c2.Y) < threshold)
                            {
                                c1.Y = c2.Y = (c1.Y + c2.Y) / 2;
                            }
                        }
                    }
                }
            }*/

            if (_linkControlPoints)
                LinkOverlappingControlPoints();

            _SelectedControlPoints.Clear();

            /*if (!_showMiddleSegment)
            {
                _MiddleSegment[3].X = _MiddleSegment[0].X + (_MiddleSegment[0].X - _MiddleSegment[2].X); 
                _MiddleSegment[3].Y = _MiddleSegment[0].Y + (_MiddleSegment[0].Y - _MiddleSegment[2].Y); 
            }*/

            int index = 0;
            foreach (CurveSegment curve in _CurveSegments)
            {
                curve.IsReactant = !curve.IsModifier && (index < ReactantCount);
                ++index;
                curve.GeneratePoints();
                if (curve.IsModifier && curve.Segments.Count > 0)
                {
                    ControlPoint cp = curve.Segments[curve.Segments.Count - 1].End;
                    RectangleF midRegion = MiddleRegion;
                    midRegion.Inflate(curve.ArrowSize, curve.ArrowSize);
                    if (!midRegion.Contains(cp.Point))
                    {
                        float minDist;
                        PointF closest;
                        if (PointDistance(_MiddleSegment[0].Point, cp.Point) < PointDistance(_MiddleSegment[1].Point, cp.Point))
                        {
                            minDist = PointDistance(_MiddleSegment[0].Point, cp.Point);
                            closest = _MiddleSegment[0].Point;
                        }
                        else
                        {
                            minDist = PointDistance(_MiddleSegment[1].Point, cp.Point);
                            closest = _MiddleSegment[1].Point;
                        }
                        PointF p;
                        foreach (CurveSegment c in _CurveSegments)
                        {
                            if (!c.IsModifier)
                            {
                                p = c.ClosestPoint(cp.Point);
                                float dist = PointDistance(p, cp.Point);
                                if (dist < minDist)
                                {
                                    minDist = dist;
                                    closest = p;
                                }
                            }
                        }

                        if (PointDistance(cp.Point, closest) > 2 * curve.ArrowSize)
                        {
                            if (cp.X < closest.X)
                                cp.X = closest.X - curve.ArrowSize;
                            else
                                cp.X = closest.X + curve.ArrowSize;

                            if (cp.Y < closest.Y)
                                cp.Y = closest.Y - curve.ArrowSize;
                            else
                                cp.Y = closest.Y + curve.ArrowSize;
                        }
                    }
                }
            }

            base.DoneModifying(Corner.None, modifyAnchoredItems);
        }

        protected bool _IsSelected;

        /// <summary>
        /// Selects or deselects the whole connector
        /// </summary>
        public override bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                if (value) Select(); else Deselect();
            }
        }
        public override void Select()
        {
            _IsSelected = true;

            bool noneSelected = true;

            if (_CurveSegments != null)
                foreach (CurveSegment c in _CurveSegments)
                    if (c.IsFound)
                    {
                        c.IsFound = false;
                        c.IsSelected = true;
                        noneSelected = false;
                    }
            if (noneSelected)
                foreach (CurveSegment c in _CurveSegments)
                {
                    c.IsSelected = true;
                }
            //Utility.BringItemToFront(this, Program.Model);
        }
        /// <summary>
        /// Deselects the curve and each segment inside it
        /// </summary>
        public override void Deselect()
        {
            _SelectedControlPoints.Clear();
            _IsSelected = false;
            if (_CurveSegments != null)
                foreach (CurveSegment c in _CurveSegments)
                    c.IsSelected = c.IsFound = false;
             
        }
        
        protected PointF _Location = new PointF();

        /// <summary>
        /// Finds the minimum point from all the segments -- O(N) on the number of segments
        /// </summary>
        public override PointF Location
        {
            get
            {
                SetLocationAndSize();
                return _Location;
            }
            set
            {
                SetLocationAndSize();

                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
                List<ControlPoint> selected = new List<ControlPoint>();
                foreach (CurveSegment c in _CurveSegments)
                    for (int i = 0; i < c.Segments.Count; ++i)
                    {
                        if (!selected.Contains(c.Segments[i].Start))
                            selected.Add(c.Segments[i].Start);

                        if (!selected.Contains(c.Segments[i].ControlPoint1))
                            selected.Add(c.Segments[i].ControlPoint1);

                        if (!selected.Contains(c.Segments[i].ControlPoint2))
                            selected.Add(c.Segments[i].ControlPoint2);

                        if (!selected.Contains(c.Segments[i].End))
                            selected.Add(c.Segments[i].End);
                    }

                for (int i = 0; i < _MiddleSegment.Length; ++i)
                {
                    if (!selected.Contains(_MiddleSegment[i]))
                        selected.Add(_MiddleSegment[i]);
                }

                for (int i = 0; i < selected.Count; ++i)
                    selected[i].Add(Utility.SubtractPoints(value,_Location));

                int index = 0;
                foreach (CurveSegment curve in _CurveSegments)
                {
                    curve.IsReactant = (index < ReactantCount);
                    ++index;
                    curve.GeneratePoints();
                }

                _Location = value;
            }
        }

        private SizeF _Dimensions;
        /// <summary>
        /// Finds the dimension using all the segments -- O(N) on the num. of segments
        /// </summary>
        public override SizeF Dimensions
        {
            get
            {
                SetLocationAndSize();
                return _Dimensions;
            }
            set
            {
                //_Dimensions = value;
            }
        }

        /// <summary>
        /// Calculates boundary from Location and Dimension
        /// </summary>
        public override RectangleF Bounds
        {
            get
            {
                SetLocationAndSize();
                return new RectangleF(_Location, _Dimensions);
            }
            set
            {
                Location = value.Location;
                Dimensions = value.Size;
            }
        }

        /// <summary>
        /// Finds the min and max points from all the segments. Sets the location and dimension memebers
        /// </summary>
        protected virtual void SetLocationAndSize()
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            if (_CurveSegments == null) return;
            PointF min = new PointF(_CurveSegments[0].MinPoint.X, _CurveSegments[0].MinPoint.Y), max = new PointF(_CurveSegments[0].MinPoint.X, _CurveSegments[0].MinPoint.Y);
            for (int i = 0; i < _CurveSegments.Count; ++i)
            {
                if (_CurveSegments[i].MinPoint.X < min.X) min.X = _CurveSegments[i].MinPoint.X;
                if (_CurveSegments[i].MaxPoint.X > max.X) max.X = _CurveSegments[i].MaxPoint.X;
                if (min.Y > _CurveSegments[i].MinPoint.Y) min.Y = _CurveSegments[i].MinPoint.Y;
                if (max.Y < _CurveSegments[i].MaxPoint.Y) max.Y = _CurveSegments[i].MaxPoint.Y;
            }
            _Location.X = min.X;
            _Location.Y = min.Y;
            _Dimensions.Width = max.X - min.X;
            _Dimensions.Height = max.Y - min.Y;
        }

        protected virtual bool AllStraight
        {
            get
            {
                foreach (CurveSegment c in _CurveSegments)
                    if (!c.IsStraight && !c.IsModifier)
                        return false;
                return true;
            }
        }

        public override ViewType View
        {
            get
            {
                return base.View;
            }
            set
            {
                base.View = value;
                if (value == ViewType.Schematic)
                {
                    ColorStart = Color.Black;
                    int k = 0;
                    foreach (CurveSegment c in _CurveSegments)
                    {
                        c.IsFound = true;
                        PointF previous = c.StartPoint;
                        for (int i = 0; i < c.Segments.Count; ++i)
                        {
                            if (c.Segments[i].End.X != previous.X && c.Segments[i].End.Y != previous.Y)
                            {
                                if (c.InsertNewSegment(new PointF(previous.X, c.Segments[i].End.Y)))
                                {
                                    if (k < _numInputs)
                                        c.Segments[c.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                                    else
                                        c.Segments[c.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                                }
                            }
                            previous = c.Segments[i].End.Point;
                        }
                        ++k;
                    }
                    StraightLines = true;
                    FixMultipleConnections();
                }
                else
                {
                    foreach (CurveSegment c in _CurveSegments)
                    {
                        c.IsFound = true;                        
                    }
                    StraightLines = false;
                }
            }
        }

        public virtual void SelectAllSegments()
        {
            foreach (CurveSegment c in _CurveSegments)
                c.IsSelected = true;
        }

        /// <summary>
        /// Makes the selected segments into straight segments        
        /// </summary>
        [CategoryAttribute("Connector Settings"), Description("Make all segments into lines")]
        public virtual bool StraightLines
        {
            get
            {
                int numSelected = 0;
                bool allstraight = true;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (!c.IsStraight)
                    {
                        ++numSelected;
                        allstraight = false;
                        if (c.IsSelected)
                        {
                            return false;
                        }
                    }
                }

                if (numSelected == 0)
                    return allstraight;

                return false;
            }
            set
            {
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;

                bool noneSelected = true;

                foreach (CurveSegment c in _CurveSegments)
                    if (c.IsSelected)
                    {
                        noneSelected = false;
                        break;
                    }

                if (noneSelected)
                    SelectAllSegments();

                foreach (CurveSegment c in _CurveSegments)
                    if (c.IsSelected)
                    {
                        c.IsStraight = value;
                        c.GeneratePoints();
                    }

                if (!value)
                {
                    _MiddleSegment[2].X = _MiddleSegment[0].X - 10;
                    _MiddleSegment[3].X = _MiddleSegment[1].X + 10;
                    _MiddleSegment[2].Y = _MiddleSegment[3].Y = _MiddleSegment[0].Y = _MiddleSegment[1].Y;
                    for (int i = 0; i < _CurveSegments.Count; ++i)
                    {
                        int n = _CurveSegments[i].Segments.Count - 1;
                        if (i < _numInputs)
                            _CurveSegments[i].Segments[n].ControlPoint2 = _MiddleSegment[2];
                        else
                            _CurveSegments[i].Segments[n].ControlPoint2 = _MiddleSegment[3];
                    }                         
                }
            }
        }


        bool _linkControlPoints;

        /// <summary>
        /// Sets a bool which will cause all close points to become the same point when DoneModify is called.
        /// If set to false, then all the control points are made into new control points, i.e. none point to 
        /// each other
        /// </summary>
        [CategoryAttribute("Connector Settings"), Description("Link overlapping points")]
        public virtual bool LinkControlPoints
        {
            get
            {
                return _linkControlPoints;
            }
            set
            {
                _linkControlPoints = value;
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
                if (!_linkControlPoints)   //remake all control points as copy of their old self
                {
                    for (int i = 0; i < _MiddleSegment.Length; ++i)
                        _MiddleSegment[i] = new ControlPoint(_MiddleSegment[i]);

                    if (!_showMiddleSegment) _MiddleSegment[0] = _MiddleSegment[1];

                    foreach (CurveSegment c in _CurveSegments)
                        for (int i = 0; i < c.Segments.Count; ++i)
                        {
                            c.Segments[i].ControlPoint1 = new ControlPoint(c.Segments[i].ControlPoint1);
                            c.Segments[i].ControlPoint2 = new ControlPoint(c.Segments[i].ControlPoint2);
                            c.Segments[i].End = new ControlPoint(c.Segments[i].End);
                            if (i < c.Segments.Count - 1)
                                c.Segments[i + 1].Start = c.Segments[i].End;
                        }

                    for (int i = 0; i < _CurveSegments.Count; ++i)
                    {
                        CurveSegment curve = _CurveSegments[i];
                        if (!curve.IsModifier)
                        {
                            if (i < _numInputs)
                            {
                                curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                                curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[0];
                            }
                            else
                            {
                                curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                                curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[1];
                            }
                        }
                    }
                }
            }
        }

        bool _showMiddleSegment;

        /// <summary>
        /// Turns on/off the middle segment. If turned on, the two ends of the midsegment are places between 
        /// the former middle control points. Vertical/Horizontal poisition is determined using their positions
        /// If turned off, then the former midsegment's center is used as the new center and formed midsegment's
        /// endpoints are new control points.
        /// </summary>
        [CategoryAttribute("Connector Settings"), Description("Show the region in the middle")]
        public virtual bool ShowMiddleSegment
        {
            get
            {
                return _showMiddleSegment;
            }
            set
            {
                if (Immobile || _showMiddleSegment == value) return;
                _showMiddleSegment = value;
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
                if (_showMiddleSegment)
                {
                    if (Math.Abs(_MiddleSegment[2].X - _MiddleSegment[3].X) > Math.Abs(_MiddleSegment[2].Y - _MiddleSegment[3].Y)) //horizontal
                    {
                        _MiddleSegment[0] = new ControlPoint(0.7F * _MiddleSegment[2].X + 0.3F * _MiddleSegment[3].X, 0.5F * _MiddleSegment[2].Y + 0.5F * _MiddleSegment[3].Y);
                        _MiddleSegment[1] = new ControlPoint(0.3F * _MiddleSegment[2].X + 0.7F * _MiddleSegment[3].X, 0.5F * _MiddleSegment[2].Y + 0.5F * _MiddleSegment[3].Y);
                        _MiddleSegment[2].Y = _MiddleSegment[3].Y = _MiddleSegment[0].Y;
                        /*if (_MiddleSegment[0].X > _MiddleSegment[1].X)
                        {
                            _MiddleSegment[2].X = _MiddleSegment[0].X + 50;
                            _MiddleSegment[3].X = _MiddleSegment[1].X - 50;
                        }
                        else
                        {
                            _MiddleSegment[2].X = _MiddleSegment[0].X - 50;
                            _MiddleSegment[3].X = _MiddleSegment[1].X + 50;
                        }*/
                    }
                    else //vertical
                    {
                        _MiddleSegment[0] = new ControlPoint(0.5F * _MiddleSegment[2].X + 0.5F * _MiddleSegment[3].X, 0.7F * _MiddleSegment[2].Y + 0.3F * _MiddleSegment[3].Y);
                        _MiddleSegment[1] = new ControlPoint(0.5F * _MiddleSegment[2].X + 0.5F * _MiddleSegment[3].X, 0.3F * _MiddleSegment[2].Y + 0.7F * _MiddleSegment[3].Y);
                        _MiddleSegment[2].X = _MiddleSegment[3].X = _MiddleSegment[0].X;
                        /*if (_MiddleSegment[0].Y > _MiddleSegment[1].Y)
                        {
                            _MiddleSegment[2].Y = _MiddleSegment[0].Y + 50;
                            _MiddleSegment[3].Y = _MiddleSegment[1].Y - 50;
                        }
                        else
                        {
                            _MiddleSegment[2].Y = _MiddleSegment[0].Y - 50;
                            _MiddleSegment[3].Y = _MiddleSegment[1].Y + 50;
                        }*/
                    }
                }
                else
                {
                    _MiddleSegment[2] = new ControlPoint(_MiddleSegment[0]);
                    _MiddleSegment[3] = new ControlPoint(_MiddleSegment[1]);
                    _MiddleSegment[0] = _MiddleSegment[1] = new ControlPoint(AddTwoScaledPoints(_MiddleSegment[0].Point, _MiddleSegment[3].Point, 0.5F, 0.5F));
                }
                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    CurveSegment curve = _CurveSegments[i];
                    if (!curve.IsModifier)
                    {
                        if (i < _numInputs)
                        {
                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                            curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[0];
                        }
                        else
                        {
                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                            curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[1];
                        }
                    }
                }
            }
        }

        bool _insertLineBreaks;

        /// <summary>
        /// Toggles a bool. If this bool is on, the next Where(..) call will cause a new point to be
        /// inserted instead of selecting a control point. 
        /// a connector is clicked on.
        /// </summary>
        [CategoryAttribute("Connector Settings"), Description("Insert a new break point")]
        public virtual bool InsertLineBreaks
        {
            get
            {
                return _insertLineBreaks;
            }
            set
            {
                _insertLineBreaks = value;
            }
        }
        /// <summary>
        /// Moves all the control points given. To be used in conjunction with SelectControlPoints
        /// </summary>
        /// <param name="SelectedControlPoints"></param>
        /// <param name="oDirection"></param>
        public override void MoveSelectedControlPoints(List<ControlPoint> SelectedControlPoints, PointF oDirection)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            for (int i = 0; i < SelectedControlPoints.Count; ++i)
            {
                SelectedControlPoints[i].Point = AddTwoPoints(SelectedControlPoints[i].Point, oDirection);
            }
        }

        /// <summary>
        /// Removes the given subsegment of the given segment
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="subsegment"></param>
        /// <returns></returns>
        public virtual bool RemoveControlPoints(int segment, int subsegment)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return false;

            if (segment < 0 || subsegment < 0) return false;

            if (segment < _CurveSegments.Count)
            {
                if (_CurveSegments[segment].Segments.Count > subsegment)
                {
                    if (subsegment == 0)
                        _CurveSegments[segment].Segments[subsegment].End = _CurveSegments[segment].Segments[subsegment + 1].End;
                    else
                        _CurveSegments[segment].Segments[subsegment].End = _CurveSegments[segment].Segments[subsegment - 1].End;

                    _CurveSegments[segment].GeneratePoints();
                }
            }
            return false;
        }

        /// <summary>
        /// connects together a possibly disconnected middle segment
        /// Primarily used for reading coordinate information and then assembling the connector
        /// </summary>
        public virtual void FixMiddleSegment()
        {
            List<PointF> reactantControlPoints = new List<PointF>(),
                         productControlPoints = new List<PointF>(),
                         endPoints = new List<PointF>();
            int index = 0;
            foreach (CurveSegment curve in _CurveSegments)
            {
                if (curve.Segments != null && curve.Segments.Count > 0)
                {
                    endPoints.Add(curve.Segments[curve.Segments.Count - 1].End.Point);
                    if (!curve.IsModifier)
                    {
                        if (index < _numInputs)
                            reactantControlPoints.Add(curve.Segments[curve.Segments.Count - 1].ControlPoint2.Point);
                        else
                            productControlPoints.Add(curve.Segments[curve.Segments.Count - 1].ControlPoint2.Point);
                    }
                }
                ++index;
            }

            if (_MiddleSegment == null || _MiddleSegment.Length != 4)
                _MiddleSegment = new ControlPoint[4];

            _MiddleSegment[0] = _MiddleSegment[1] = new ControlPoint(Utility.CenterPoint(endPoints));
            _MiddleSegment[2] = new ControlPoint(Utility.CenterPoint(reactantControlPoints));
            _MiddleSegment[3] = new ControlPoint(Utility.CenterPoint(productControlPoints));
            
            float x = _MiddleSegment[0].X - (_MiddleSegment[3].X - _MiddleSegment[1].X);
            float y = _MiddleSegment[0].Y - (_MiddleSegment[3].Y - _MiddleSegment[1].Y);
            _MiddleSegment[2].X = (_MiddleSegment[2].X + x) / 2.0f;
            _MiddleSegment[2].Y = (_MiddleSegment[2].Y + y) / 2.0f;
            
            _MiddleSegment[3].X = _MiddleSegment[1].X - (_MiddleSegment[2].X - _MiddleSegment[0].X);
            _MiddleSegment[3].Y = _MiddleSegment[1].Y - (_MiddleSegment[2].Y - _MiddleSegment[0].Y);
            
            index = 0;
            foreach (CurveSegment curve in _CurveSegments)
                if (curve.Segments != null && curve.Segments.Count > 0)
                {
                    if (index < _numInputs)
                    {
                        if (!curve.IsModifier)
                        {
                            curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[0];
                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                        }
                    }
                    else
                    {
                        if (!curve.IsModifier)
                        {
                            curve.Segments[curve.Segments.Count - 1].End = _MiddleSegment[1];
                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                        }
                    }
                    curve.IsReactant = (index < ReactantCount);
                    ++index;
                    curve.GeneratePoints();
                }
        }

        /// <summary>
        /// Sets up the location of the midsegment -- used only by the constructor 
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        protected virtual void SetupMiddleSegment(List<IDrawable> listOfDrawables, int numInputs)
        {
            if (listOfDrawables.Count < 2 || numInputs < 1 || numInputs >= listOfDrawables.Count) return;

            List<IDrawable> fullList = new List<IDrawable>(),
                            inputList = new List<IDrawable>(),  //get the unique items only
                            outputList = new List<IDrawable>();
            
            for (int i = 0; i < listOfDrawables.Count; ++i)
            {
                if (i < numInputs)
                {
                    if (!inputList.Contains(listOfDrawables[i]))
                    {
                        inputList.Add(listOfDrawables[i]);
                        fullList.Add(listOfDrawables[i]);
                    }
                }
                else
                {
                    if (!outputList.Contains(listOfDrawables[i]))
                    {
                        outputList.Add(listOfDrawables[i]);
                        fullList.Add(listOfDrawables[i]);
                    }
                }
            }

            PointF midpt1 = new PointF(0, 0), midpt2 = new PointF(0, 0), midpt = new PointF(0, 0);
            foreach (IDrawable ig in fullList) //getting the midpoint for inputs and outputs
            {
                midpt.X += ig.Location.X + ig.Dimensions.Width / 2;
                midpt.Y += ig.Location.Y + ig.Dimensions.Height / 2;
            }

            midpt.X /= fullList.Count;
            midpt.Y /= fullList.Count;

            PointF[] closePointsIn = new PointF[inputList.Count],
                     closePointsOut = new PointF[outputList.Count];
            
            for (int i = 0; i < inputList.Count; ++i)
            {
                closePointsIn[i] = Utility.GetPointOnEdge(inputList[i].Bounds, midpt, 0);             
            }
            for (int i = 0; i < outputList.Count; ++i)
            {
                closePointsOut[i] = Utility.GetPointOnEdge(outputList[i].Bounds, midpt, 0);
            }

            //find the midpoints again
            midpt1 = new PointF(0, 0); midpt2 = new PointF(0, 0); midpt = new PointF(0, 0);
            
            foreach (PointF p in closePointsIn) //getting the midpoint for inputs and outputs
            {
                midpt1.X += p.X;
                midpt1.Y += p.Y;
            }    
            foreach (PointF p in closePointsOut) //getting the midpoint for inputs and outputs
            {
                midpt2.X += p.X;
                midpt2.Y += p.Y;
            }

            midpt1.X /= inputList.Count;   //midpoint between inputs points
            midpt1.Y /= inputList.Count;
            
            midpt2.X /= outputList.Count;   //midpoint between output points
            midpt2.Y /= outputList.Count;

            midpt = AddTwoScaledPoints(midpt1, midpt2, 0.5F, 0.5F);
            _MiddleSegment[0] = _MiddleSegment[1] = new ControlPoint(midpt);
            _MiddleSegment[2] = new ControlPoint(AddTwoScaledPoints(midpt, midpt2, 1.5F, -0.5F));
            _MiddleSegment[3] = new ControlPoint(AddTwoScaledPoints(midpt, midpt2, 0.5F, 0.5F));

            bool loops = false;

            foreach (IDrawable id in inputList)  //are there any self-loops?
            {
                if (outputList.Contains(id))
                {
                    loops = true;
                    break;
                }
            }

            if (loops)
            {
                if (PointDistance(_MiddleSegment[2].Point, _MiddleSegment[3].Point) < 5)  //self loop
                {
                    _MiddleSegment[2].Y = _MiddleSegment[3].Y = _MiddleSegment[0].Y = listOfDrawables[0].Location.Y - Math.Min(listOfDrawables[0].Dimensions.Height, listOfDrawables[0].Dimensions.Width);
                    _MiddleSegment[2].X -= 5;// listOfDrawables[0].Dimensions.Width / 2;
                    _MiddleSegment[3].X += 5;// listOfDrawables[0].Dimensions.Width / 2;
                }
                else
                {
                    float dx = _MiddleSegment[0].X - _MiddleSegment[2].X,
                          dy = _MiddleSegment[0].Y - _MiddleSegment[2].Y;

                    _MiddleSegment[2].X = _MiddleSegment[0].X + dy;
                    _MiddleSegment[3].X = _MiddleSegment[0].X - dy;
                    _MiddleSegment[2].Y = _MiddleSegment[0].Y + dx;
                    _MiddleSegment[3].Y = _MiddleSegment[0].Y - dx;
                }
            }
        }

        /// <summary>
        /// Fixes the case where a single object has multiple connections to it -- used only by constructor
        /// </summary>
        protected virtual void FixMultipleConnections()
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            PointF center;
            float invslope, size;
            
            for (int i = 1; i < _CurveSegments.Count; ++i)
            {
                for (int j = 0; j < i; ++j)
                    if (_CurveSegments[i].StartNode == _CurveSegments[j].StartNode)
                    {
                        center = new PointF(_CurveSegments[i].StartNode.Location.X + _CurveSegments[i].StartNode.Dimensions.Width / 2, _CurveSegments[i].StartNode.Location.Y + _CurveSegments[i].StartNode.Dimensions.Height / 2);
                        //size = Math.Max(_Segments[i].StartNode.Dimensions.Width, _Segments[i].StartNode.Dimensions.Height);
                        size = 10;

                        invslope = Math.Abs((_CurveSegments[i].Segments[0].ControlPoint1.X - center.X) / (_CurveSegments[i].Segments[0].ControlPoint1.Y - center.Y));
                        _CurveSegments[i].Segments[0].ControlPoint1.X -= size;
                        _CurveSegments[j].Segments[0].ControlPoint1.X += size;
                        _CurveSegments[i].Segments[0].ControlPoint1.Y -= Math.Min(size, size * (invslope));
                        _CurveSegments[j].Segments[0].ControlPoint1.Y += Math.Min(size, size * (invslope));

                        if (_CurveSegments[i].IsStraight && _CurveSegments[j].IsStraight)
                        {
                            if (PointDistance(_CurveSegments[i].Segments[0].End.Point, _CurveSegments[j].Segments[0].End.Point) < 1)
                            {
                                if (_CurveSegments[i].Segments[0].End.X == _CurveSegments[i].Segments[1].End.X)
                                {
                                    _CurveSegments[i].Segments[0].End.Y += 5;
                                    _CurveSegments[j].Segments[0].End.Y -= 5;
                                }
                                else
                                {
                                    _CurveSegments[i].Segments[0].End.X += 5;
                                    _CurveSegments[j].Segments[0].End.X -= 5;
                                }
                            }
                        }

                        _CurveSegments[i].GeneratePoints();
                        _CurveSegments[j].GeneratePoints();

                    }
            }
        }

        protected virtual void MakeSegments(List<IDrawable> list, int inputs)
        {
            if (list.Count < 2 || inputs < 1 || inputs >= list.Count) return;

            _numInputs = inputs;
            _CurveSegments = new List<CurveSegment>();

            int k = 0;

            _MiddleSegment = new ControlPoint[4];  //first two define the line, last two points are the control points

            SetupMiddleSegment(list, inputs);

            if (list.Count == 2)  //just two points to connect
            {
                _CurveSegments.Add(new CurveSegment(list[0], new ControlPoint(_MiddleSegment[2]), _MiddleSegment[2], _MiddleSegment[0]));
                _CurveSegments.Add(new CurveSegment(list[1], new ControlPoint(_MiddleSegment[3]), _MiddleSegment[3], _MiddleSegment[1]));
            }
            else  //multiple points to connect
            {
                k = 0;
                foreach (IDrawable d in list)
                {
                    PointF center = new PointF(d.Location.X + d.Dimensions.Width / 2, d.Location.Y + d.Dimensions.Height / 2);
                    if (k < inputs)
                    {
                        _CurveSegments.Add(new CurveSegment(
                            d,
                            new ControlPoint(AddTwoScaledPoints(center, _MiddleSegment[2].Point, 0.5F, 0.5F)),
                            _MiddleSegment[2],
                            _MiddleSegment[0]
                            ));
                    }
                    else
                    {
                        _CurveSegments.Add(new CurveSegment(
                            d,
                            new ControlPoint(AddTwoScaledPoints(center, _MiddleSegment[3].Point, 0.5F, 0.5F)),
                            _MiddleSegment[3],
                            _MiddleSegment[1]
                            ));
                    }
                    ++k;
                }
            }
            FixMultipleConnections();
            k = 0;
            foreach (CurveSegment c in _CurveSegments)
            {
                c.IsReactant = (k < ReactantCount);
                c.CleanUpDiagram();
                ++k;
            }
        }

        /// <summary>
        /// Get the middle point
        /// </summary>
        [CategoryAttribute("Middle Region"), Description("The middle region of this connection")]
        public override RectangleF MiddleRegion
        {
            get
            {
                RectangleF rect = new RectangleF();
                if (_MiddleSegment == null || _MiddleSegment.Length < 4) return rect;

                for (int i = 0; i < 4; ++i)
                    if (_MiddleSegment[i] == null) _MiddleSegment[i] = new ControlPoint();

                rect = new RectangleF(Math.Min(_MiddleSegment[0].X, _MiddleSegment[1].X), Math.Min(_MiddleSegment[0].Y, _MiddleSegment[1].Y), Math.Abs(_MiddleSegment[1].X - _MiddleSegment[0].X), Math.Abs(_MiddleSegment[1].Y - _MiddleSegment[0].Y));
                rect.Inflate(7, 7);
                return rect;
            }
        }

        /// <summary>
        /// The slope of the curve at the center
        /// </summary>
        /// <returns></returns>
        public virtual double MiddleSlope
        {
            get
            {
                bool AllStraight = true;
                foreach (CurveSegment c in _CurveSegments)
                    if (!c.IsStraight)
                    {
                        AllStraight = false;
                        break;
                    }

                if (AllStraight)
                {
                    PointF p1 = _CurveSegments[0].Segments[_CurveSegments[0].Segments.Count - 1].Start.Point,
                           p2 = _CurveSegments[_CurveSegments.Count - 1].Segments[_CurveSegments[_CurveSegments.Count - 1].Segments.Count - 1].Start.Point;
                    if (p1.X == p2.X) return 1000.0;
                    return (p1.Y - p2.Y) / (p1.X - p2.X);
                }
                if (_MiddleSegment[3].X == _MiddleSegment[2].X) return 1000.0;
                return (_MiddleSegment[3].Y - _MiddleSegment[2].Y) / (_MiddleSegment[3].X - _MiddleSegment[2].X);
            }
        }

        /// <summary>
        /// default constructor -- no segments, item is null
        /// </summary>
        public BezierConnector()
        {
            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _CurveSegments = new List<CurveSegment>();
            _MiddleSegment = new ControlPoint[4];
            _showMiddleSegment = false;
            _insertLineBreaks = false;
            _ShowLabel = SHOW_LABEL;
        }

        /// <summary>
        /// Constructor -- needs to know the items to connect and how many inputs
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(List<IDrawable> list, int inputs, bool makeItem)
        {
            if (list == null) list = new List<IDrawable>();

            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _showMiddleSegment = false;            
            _insertLineBreaks = false;
            _ShowLabel = SHOW_LABEL;

            if (inputs < 1)
            {
                IDrawable waste = Model.Empty.GenerateAlias();                
                if (list.Count > 0)
                {
                    waste.Location = new PointF(list[0].Bounds.Right + 50, list[0].Location.Y);
                }
                else
                    waste.Location = new PointF(Utility.Panel.Location.X + 50, Utility.Panel.Location.Y + 100);
                list.Add(waste);
                list.Reverse();
                inputs = 1;
            }
            if (inputs == list.Count)
            {
                IDrawable source = Model.Empty.GenerateAlias();
                if (list.Count > 0)
                {
                    source.Location = new PointF(list[0].Bounds.Right, list[0].Location.Y - 50);
                }
                else
                    source.Location = new PointF(Utility.Panel.Location.X + 100, Utility.Panel.Location.Y + 50);
                list.Add(source);
            }

            if (list.Count < 2 || inputs < 1 || inputs >= list.Count) return;  //not possible

            MakeSegments(list, inputs);

            //LinkControlPoints = false;

            int i = 0;
            foreach (IDrawable item in list)
            {
                if (item is IGlyph) //inform the iglyph about its new connection
                {
                    if (i < inputs)                    
                        ((IGlyph)item).AddOutgoingConnection(this);    //input node
                    else
                        ((IGlyph)item).AddIncomingConnection(this);  //output node                    
                }
                ++i;
            }

            //SyncParent();            
            ArrowHead = ArrowHeadType.Arrow;

            MakeItem(list, inputs, makeItem);

            Name = "J0";

            LoadDefaults(DEFAULT);
        }
        /// <summary>
        /// is this reaction a regulatory reaction
        /// </summary>
        public override bool IsRegulation
        {
            get
            {
                foreach (CurveSegment cs in _CurveSegments)
                {
                    if (cs.StartNode is Connector)
                        return true;
                }
                if (_Item is Edge) return ((Edge)_Item).IsRegulation;
                return false;
            }
        }
        /// <summary>
        /// Constructor -- needs to know the items to connect and how many inputs
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(List<IDrawable> list, int inputs)
            : this(list,inputs,true)
        {
            Name = "J0";
            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _showMiddleSegment = false;
            _insertLineBreaks = false;
            _ShowLabel = SHOW_LABEL;
        }

        /// <summary>
        /// Constructor -- provide a list of inputs and outputs
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(List<IDrawable> inputList, List<IDrawable> outputList, bool makeItem)
        {
            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _showMiddleSegment = false;
            _insertLineBreaks = false;
            _ShowLabel = SHOW_LABEL;

            if (inputList == null) inputList = new List<IDrawable>();
            if (outputList == null) outputList = new List<IDrawable>();

            int inputs = inputList.Count;

            if (inputs < 1)
            {
                IDrawable source = Model.Empty.GenerateAlias();
                if (outputList.Count > 0 && outputList[0] != null)
                {
                    source.Location = new PointF(outputList[0].Bounds.Right, outputList[0].Location.Y - 50);
                }
                else
                    source.Location = new PointF(Location.X + 30, Location.Y + 30);
                
                inputList.Add(source);
                
                inputs = 1;
            }
            if (outputList.Count < 1)
            {
                IDrawable waste = Model.Empty.GenerateAlias();
                if (inputList.Count > 0 && inputList[0] != null)
                {
                    waste.Location = new PointF(inputList[0].Bounds.Right + 50, inputList[0].Location.Y);
                }
                else
                    waste.Location = new PointF(Location.X + 50, Location.Y + 50);
                
                outputList.Add(waste);
            }
            List<IDrawable> list = inputList;
            list.AddRange(outputList);

            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _showMiddleSegment = false;
            _insertLineBreaks = false;

            if (list.Count < 2 || inputs < 1 || inputs >= list.Count) return;  //not possible

            MakeSegments(list, inputs);

            //LinkControlPoints = false;

            int i = 0;
            foreach (IDrawable item in list)
            {
                if (item is IGlyph) //inform the iglyph about its new connection
                {
                    if (i < inputs)
                        ((IGlyph)item).AddOutgoingConnection(this);    //input node
                    else
                        ((IGlyph)item).AddIncomingConnection(this);  //output node

                }
                ++i;
            }

            //SyncParent();
            ArrowHead = ArrowHeadType.Arrow;

            MakeItem(list, inputs, makeItem);

            Name = "J0";

            LoadDefaults(DEFAULT);
        }

        /// <summary>
        /// Constructor -- provide a list of inputs and outputs
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(List<IDrawable> inputList, List<IDrawable> outputList)
            : this(inputList, outputList, true)
        {
        }

        /// <summary>
        /// Constructor -- provide a single of input and output
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(IDrawable input, IDrawable output, bool makeItem)
        {
            if (input == null)
            {
                input = Model.Empty.GenerateAlias();
                if (output != null)
                {
                    input.Location = new PointF(output.Bounds.Right, output.Location.Y - 50);
                }
                else
                    input.Location = new PointF(Utility.Panel.Location.X + 100, Utility.Panel.Location.Y + 50);

                Program.Model.AddToModel(input);
            }

            if (output == null)
            {
                output = Model.Empty.GenerateAlias();
                if (input != null)
                {
                    output.Location = new PointF(input.Bounds.Right, input.Location.Y - 50);
                }
                else
                    output.Location = new PointF(Utility.Panel.Location.X + 100, Utility.Panel.Location.Y + 50);

                Program.Model.AddToModel(output);
            }

            int inputs = 1;
            List<IDrawable> list = new List<IDrawable>(2);
            list.Add(input);
            list.Add(output);

            _fThickness = 2f;
            _oFillColorStart = _oFillColorEnd = Color.DeepSkyBlue;
            _SelectedControlPoints = new List<ControlPoint>();
            _showMiddleSegment = false;
            _insertLineBreaks = false;
            _ShowLabel = SHOW_LABEL;

            if (list.Count < 2 || inputs < 1 || inputs >= list.Count) return;  //not possible

            MakeSegments(list, inputs);

            //LinkControlPoints = false;

            int i = 0;
            foreach (IDrawable item in list)
            {
                if (item is IGlyph) //inform the iglyph about its new connection
                {
                    if (i < inputs)
                        ((IGlyph)item).AddOutgoingConnection(this);    //input node
                    else
                        ((IGlyph)item).AddIncomingConnection(this);  //output node
                }
                ++i;
            }

            //SyncParent();
            ArrowHead = ArrowHeadType.Arrow;

            MakeItem(input, output, makeItem);

            Name = "J0";

            LoadDefaults(DEFAULT);
        }

        /// <summary>
        /// Load the default features of this bezier connector
        /// </summary>
        public override void LoadDefaults(IDrawable drawable)
        {
            BezierConnector Default = drawable as BezierConnector;
            if (Default != null)
            {
                ColorEnd = Default.ColorEnd;
                ColorStart = Default.ColorStart;
                SelectedColor = Default.SelectedColor;
                DoubleSelectedColor = Default.DoubleSelectedColor;
                Thickness = Default.Thickness;
                //ShowText = Default.ShowText;
                if (ShowMiddleSegment != Default.ShowMiddleSegment)
                    ShowMiddleSegment = Default.ShowMiddleSegment;
                TextColor = Default.TextColor;
                if (View != Default.View)
                    View = Default.View;
                MiddleDecorator = Default.MiddleDecorator;
                LineType = Default.LineType;
                if (LinkControlPoints != Default.LinkControlPoints)
                    LinkControlPoints = Default.LinkControlPoints;
            }
            
            if (_CurveSegments != null && _CurveSegments.Count > _numInputs && _CurveSegments[_numInputs].StartNode is PartGlyph)
            {
                _MiddleSegment[0].Y = _CurveSegments[0].StartNode.Center.Y;
                _MiddleSegment[0].X = _CurveSegments[_numInputs].StartNode.Center.X;
                foreach (CurveSegment c in _CurveSegments)
                {
                    c.IsStraight = true;
                    c.GeneratePoints();
                }
            }
        }

        /// <summary>
        /// Constructor -- provide a single of input and output
        /// </summary>
        /// <param name="list"></param>
        /// <param name="inputs"></param>
        public BezierConnector(IDrawable input, IDrawable output)
            : this(input, output, true)
        {
        }


        /// <summary>
        /// makes the setup clean -- basically a remake
        /// </summary>
        public override void MakePretty()
        {
            if (Immobile || _CurveSegments == null) return;

            try
            {
                List<IDrawable> list = new List<IDrawable>(_CurveSegments.Count);
                //ArrowHeadType[] arrows = new ArrowHeadType[_Segments.Count];

                bool allStraight = true;
                foreach (CurveSegment c in _CurveSegments)
                    if (!c.IsStraight)
                        allStraight = false;

                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    //arrows[i] = _Segments[i].ArrowHead;
                    list.Add(_CurveSegments[i].StartNode);
                    //_Segments[i] = null;   //force delete
                }

                SetupMiddleSegment(list, _numInputs);

                if (_CurveSegments.Count == 2)  //just two points to connect
                {
                    _CurveSegments[0].Segments.Clear();

                    _CurveSegments[0].Segments.Add(
                        new CurveSegment.Segment(
                            new ControlPoint(_CurveSegments[0].StartNode.Location),
                            new ControlPoint(_MiddleSegment[2]),
                            _MiddleSegment[2],
                            _MiddleSegment[0]));

                    _CurveSegments[0].GeneratePoints();

                    _CurveSegments[1].Segments.Clear();
                    _CurveSegments[1].Segments.Add(
                        new CurveSegment.Segment(
                            new ControlPoint(_CurveSegments[1].StartNode.Location),
                            new ControlPoint(_MiddleSegment[3]),
                            _MiddleSegment[3],
                            _MiddleSegment[1]));

                    _CurveSegments[1].GeneratePoints();
                }
                else  //multiple points to connect
                {
                    for (int i = 0; i < _CurveSegments.Count; ++i)
                    {
                        CurveSegment curve = _CurveSegments[i];

                        IDrawable d = curve.StartNode;

                        PointF center = d.Center;

                        if (curve.IsModifier)
                        {
                            PointF endpt = Utility.GetPointOnEdge(MiddleRegion, center, 1f);
                            PointF midpt = AddTwoScaledPoints(center, endpt, 0.5f, 0.5f);

                            curve.Segments.Clear();
                            curve.Segments.Add(
                                new CurveSegment.Segment(
                                    new ControlPoint(curve.StartNode.Location),
                                    new ControlPoint(midpt), 
                                    new ControlPoint(midpt), 
                                    new ControlPoint(endpt)));
                        }
                        else
                        {

                            if (i < _numInputs)
                            {
                                curve.Segments.Clear();

                                curve.Segments.Add(
                                    new CurveSegment.Segment(
                                        new ControlPoint(curve.StartNode.Location),
                                        new ControlPoint(AddTwoScaledPoints(center, _MiddleSegment[2].Point, 0.5F, 0.5F)),
                                        _MiddleSegment[2],
                                        _MiddleSegment[0]));

                                curve.GeneratePoints();
                            }
                            else
                            {
                                curve.Segments.Clear();

                                curve.Segments.Add(
                                    new CurveSegment.Segment(
                                        new ControlPoint(curve.StartNode.Location),
                                        new ControlPoint(AddTwoScaledPoints(center, _MiddleSegment[3].Point, 0.5F, 0.5F)),
                                        _MiddleSegment[3],
                                        _MiddleSegment[1]));

                                curve.GeneratePoints();
                            }
                        }
                        if (curve.IsStraight && allStraight)
                        {
                            PointF previous = curve.StartPoint;
                            for (int j = 0; j < curve.Segments.Count; ++j)
                            {
                                if (curve.Segments[j].End.X != previous.X && curve.Segments[j].End.Y != previous.Y)
                                {
                                    if (curve.InsertNewSegment(new PointF(previous.X, curve.Segments[j].End.Y)))
                                    {
                                        if (i < _numInputs)
                                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
                                        else
                                            curve.Segments[curve.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
                                    }
                                }
                                previous = curve.Segments[j].End.Point;
                                if (j < curve.Segments.Count - 1)
                                    curve.Segments[j].Start = curve.Segments[j].End;
                            }
                        }
                    }
                }

                FixMultipleConnections();
                int k = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    c.IsReactant = (k < ReactantCount);
                    c.CleanUpDiagram();
                    ++k;
                }
                //SyncParent();
            }
            catch (Exception) 
            {
            }
        }
        /// <summary>
        /// Inserts a new input item. Inserts this item into the segments list at the front
        /// </summary>
        /// <param name="item"></param>
        public override void AddReactant(IDrawable item)
        {
            if (FixedReaction) return;
            try
            {
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null || item == null) return;
                PointF center = new PointF(item.Location.X + item.Dimensions.Width / 2, item.Location.Y + item.Dimensions.Height / 2);
                PointF midpt = AddTwoScaledPoints(center, _MiddleSegment[0].Point, 0.5F, 0.5F);
                CurveSegment c = new CurveSegment(item, new ControlPoint(midpt), _MiddleSegment[2], _MiddleSegment[0]);
                c.IsReactant = true;
                _CurveSegments.Insert(0, c);
                ++_numInputs;
                if (item is IGlyph)
                {
                    ((IGlyph)item).AddOutgoingConnection(this);
                }
                if (_Item != null && _Item is Edge && item.Item != null)
                    (_Item as Edge).AddReactant((Node)item.Item);  //important -- update the model
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// inserts a new item into the segments list at the end
        /// </summary>
        /// <param name="item"></param>
        public override void AddProduct(IDrawable item)
        {
            if (FixedReaction) return;
            try
            {
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null || item == null) return;
                PointF center = new PointF(item.Location.X + item.Dimensions.Width / 2, item.Location.Y + item.Dimensions.Height / 2);
                PointF midpt = AddTwoScaledPoints(center, _MiddleSegment[1].Point, 0.5F, 0.5F);
                CurveSegment c = new CurveSegment(item, new ControlPoint(midpt), _MiddleSegment[3], _MiddleSegment[1]);
                c.IsReactant = false;
                _CurveSegments.Add(c);
                if (item is IGlyph)
                {
                    ((IGlyph)item).AddIncomingConnection(this);
                }
                if (_Item != null && _Item is Edge && item.Item != null)
                    (_Item as Edge).AddProduct((Node)item.Item);  //important -- update the model
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Inserts a new modifier item. Inserts this item into the segments list at the front
        /// </summary>
        /// <param name="item"></param>
        public override void AddModifier(IDrawable item)
        {
            if (FixedReaction) return;
            try
            {
                if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null || item == null) return;
                PointF center = item.Center;
                PointF endpt = Utility.GetPointOnEdge(MiddleRegion, center, 1f);
                PointF midpt = AddTwoScaledPoints(center, endpt, 0.5f, 0.5f);
                CurveSegment c = new CurveSegment(item, new ControlPoint(midpt), new ControlPoint(midpt), new ControlPoint(endpt));
                
                c.IsModifier = true;
                c.ArrowHead = ArrowHeadType.Circle;

                _CurveSegments.Insert(0, c);
                ++_numInputs;
                if (item is IGlyph)
                {
                    ((IGlyph)item).AddOutgoingConnection(this);
                }
                if (_Item != null && _Item is Edge && item.Item != null)
                    (_Item as Edge).AddModifier((Node)item.Item);  //important -- update the model
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Removes segments that are selected. If ALL segments are selected, then returns false
        /// returns true is some of the segments were successfully removed. 
        /// Returns false if there was any invalid request
        /// </summary>
        public virtual bool RemoveSelectedCurves()
        {
            return RemoveSelectedCurves(true);
        }

        /// <summary>
        /// Removes segments that are selected. If ALL segments are selected, then returns false
        /// returns true is some of the segments were successfully removed. 
        /// Returns false if there was any invalid request. Providdes an option of whether or
        /// not to remove the connected nodes
        /// </summary>
        protected virtual bool RemoveSelectedCurves(bool removeConnections)
        {
            if (_CurveSegments == null || _MiddleSegment == null) return false;

            try
            {
                //check if all the segments are selected
                int nSelectedSegments = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsSelected) ++nSelectedSegments;
                }

                bool segmentRemoved = false;

                //must have atleast 1 input and 1 output
                if (_CurveSegments.Count > 2 && nSelectedSegments < _CurveSegments.Count)
                {
                    for (int i = 0; i < _CurveSegments.Count; ++i)
                    {
                        CurveSegment curve = _CurveSegments[i];

                        if (curve.IsSelected)
                        {
                            if (i < _numInputs && _numInputs > 1)   //if selected segment is an input and numInputs is 2 or more
                            {
                                if (_Item != null && curve.StartNode != null) //update internal model
                                {
                                    if (curve.StartNode.Item is Node)
                                    {
                                        ((Edge)_Item).RemoveReactant((Node)curve.StartNode.Item);  //important -- update the model
                                    }
                                    else
                                        if (curve.StartNode.Item is Edge)  //this connector is modifying another
                                        {
                                            foreach (Node n in ((Edge)_Item).Reactants) //so all input nodes of this edge are modifying the other edge
                                            {
                                                ((Edge)curve.StartNode.Item).RemoveModifier(n);
                                            }
                                        }
                                }
                                if (removeConnections && curve.StartNode is IGlyph)  //remove the link from the object to the connector
                                {
                                    ((IGlyph)curve.StartNode).RemoveConnector(this);
                                }
                                _CurveSegments.RemoveAt(i);
                                i = -1;
                                _numInputs--;
                                segmentRemoved = true;
                            }
                            else
                            {
                                if (i >= _numInputs && _CurveSegments.Count > _numInputs + 1)  //if selected segment is output
                                {
                                    if (_Item != null && curve.StartNode != null) //update internal model
                                    {
                                        if (curve.StartNode.Item is Node)
                                        {
                                            ((Edge)_Item).RemoveProduct((Node)curve.StartNode.Item);  //important -- update the model
                                        }
                                        else
                                            if (curve.StartNode.Item is Edge)  //this connector is modifying another
                                            {
                                                foreach (Node n in ((Edge)_Item).Reactants) //so all input nodes of this edge are modifying the other edge
                                                {
                                                    ((Edge)curve.StartNode.Item).RemoveModifier(n);
                                                }
                                            }
                                    }
                                    if (removeConnections && curve.StartNode is IGlyph)   //remove the link from the object to the connector
                                    {
                                        ((IGlyph)curve.StartNode).RemoveConnector(this);
                                    }
                                    _CurveSegments.RemoveAt(i);
                                    i = -1;
                                    segmentRemoved = true;
                                }
                                else
                                {
                                    segmentRemoved = false;
                                }
                            }
                        }
                    }
                }

                if (!segmentRemoved)   //remove all the links
                {
                    Remove();
                }
                int k = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    c.IsReactant = k < ReactantCount;
                    ++k;
                }
                return segmentRemoved;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public override void Remove()
        {
            base.Remove();
            try
            {
                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    CurveSegment curve = _CurveSegments[i];

                    if (curve.StartNode is IGlyph)   //remove the link from the object to the connector
                    {
                        ((IGlyph)curve.StartNode).RemoveConnector(this);
                    }
                }
            }
            catch (Exception)
            {
            }
        }

        #region IDrawable Members

        /// <summary>
        /// Checks whether any of the curves intersects the given rectangle
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public override bool IntersectsWith(RectangleF rect)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return false;
            Where(rect);
            //the following dirty code checks whether the rect meets the line
            bool midSegmentSelected = false;
            if (_MiddleSegment[0].Y == _MiddleSegment[1].Y)  //horizontal
            {
                midSegmentSelected =
                        (!(rect.Location.X > Math.Max(_MiddleSegment[0].X, _MiddleSegment[1].X) ||
                          (rect.Location.X + rect.Width) < Math.Min(_MiddleSegment[0].X, _MiddleSegment[1].X))
                        &&
                          _MiddleSegment[0].Y > rect.Location.Y && _MiddleSegment[0].Y < (rect.Location.Y + rect.Height));

            }
            else                            //vertical 
            {
                midSegmentSelected =
                       (!(rect.Location.Y > Math.Max(_MiddleSegment[0].Y, _MiddleSegment[1].Y) ||
                          (rect.Location.Y + rect.Height) < Math.Min(_MiddleSegment[0].Y, _MiddleSegment[1].Y))
                        &&
                          _MiddleSegment[0].X > rect.Location.X && _MiddleSegment[0].X < (rect.Location.X + rect.Width));
            }

            bool selected = false;

            foreach (CurveSegment c in _CurveSegments)
                if (c.Contains(rect) || midSegmentSelected)
                {
                    selected = true;
                    c.IsFound = true;
                }

            foreach (ControlPoint p in _MiddleSegment)
                if (rect.Contains(p.Point))
                    selected = true;

            return selected;
        }
        /// <summary>
        /// Checks whether the given point is near the curve
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        public override bool Contains(PointF location)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return false;
            //if (SelectControlPointsAt(location) != null) return true;

            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.Contains(location))
                {
                    c.IsFound = true;
                    return true;
                }
            }

            for (int i = 0; i < 4; ++i) if (_MiddleSegment[i] == null) _MiddleSegment[i] = new ControlPoint();

            //then check the mid segment
            bool midSegmentselected = false;
            if (_MiddleSegment[0].Y == _MiddleSegment[1].Y)  //horizontal
            {
                if (_MiddleSegment[0].X < _MiddleSegment[1].X)
                    midSegmentselected = (location.X >= _MiddleSegment[0].X && location.X <= _MiddleSegment[1].X && Math.Abs(location.Y - _MiddleSegment[0].Y) < 6);
                else
                    midSegmentselected = (location.X >= _MiddleSegment[1].X && location.X <= _MiddleSegment[0].X && Math.Abs(location.Y - _MiddleSegment[0].Y) < 6);
            }
            else                            //vertical 
            {
                if (_MiddleSegment[0].Y < _MiddleSegment[1].Y)
                    midSegmentselected = (location.Y >= _MiddleSegment[0].Y && location.Y <= _MiddleSegment[1].Y && Math.Abs(location.X - _MiddleSegment[0].X) < 6);
                else
                    midSegmentselected = (location.Y >= _MiddleSegment[1].Y && location.Y <= _MiddleSegment[0].Y && Math.Abs(location.X - _MiddleSegment[0].X) < 6);
            }
            if (midSegmentselected)
            {
                foreach (CurveSegment c in _CurveSegments)
                    c.IsFound = true;

                return true;
            }
            else
                return false;
        }
        /// <summary>
        /// Inserts a new segment (breaks the curve) at the given location
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public void InsertLineBreak(PointF oPoint)
        {
            if (Immobile) return;

            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;
            
            CurveSegment closest = _CurveSegments[0];
            double minDist = closest.DistanceFrom(oPoint), temp;

            int k = 0, bestK = 0;

            foreach (CurveSegment c in _CurveSegments)
            {
                temp = c.DistanceFrom(oPoint);
                if (temp < minDist)
                {
                    minDist = temp;
                    closest = c;
                    bestK = k;
                }
                ++k;
            }

            closest.InsertNewSegment(oPoint);
            _insertLineBreaks = false;

            if (bestK < _numInputs)
                closest.Segments[closest.Segments.Count - 1].ControlPoint2 = _MiddleSegment[2];
            else
                closest.Segments[closest.Segments.Count - 1].ControlPoint2 = _MiddleSegment[3];
            
        }

        /// <summary>
        /// moves all the points by the given amount
        /// </summary>
        /// <param name="direction"></param>
        public override void Translate(PointF direction)
        {
            SelectAllControlPoints();
            foreach (ControlPoint cp in _SelectedControlPoints)
            {
                cp.Point = Utility.AddPoints(cp.Point, direction);
            }            
            if (Parent != null)
            {
                Parent.ChildChanged(this);
            }
            foreach (IDrawable id in _AnchoredItems)
            {
                id.Translate(direction);
            }
        }

        protected static Color _oSelectedColor = Color.DarkOrange;
        protected static Color _oDoubleSelected = Color.DarkRed;
        protected static float _fSelectedThickness = 2.0f;
        protected DashStyle _DashStyle = DashStyle.Solid;

        [CategoryAttribute("Connector Settings"), Description("select dashed line or solid line")]
        public DashStyle LineType
        {
            get { return _DashStyle; }
            set { _DashStyle = value; }
        }

        [CategoryAttribute("Connector Settings"), Description("Color when selected")]
        public Color SelectedColor
        {
            get { return _oSelectedColor; }
            set { _oSelectedColor = value; }
        }
        [CategoryAttribute("Connector Settings"), Description("Color when subsegment is selected")]
        public Color DoubleSelectedColor
        {
            get { return _oDoubleSelected; }
            set { _oDoubleSelected = value; }
        }
        [CategoryAttribute("Connector Settings"), Description("thickness when selected")]
        public float SelectedThickness
        {
            get { return _fSelectedThickness; }
            set { _fSelectedThickness = value; }
        }
        /// <summary>
        /// Draws a white curve below the colored curve. 
        /// </summary>
        /// <param name="g">graphis device to draw on</param>
        public override void Draw(Graphics g)
        {
            //draw the bezier in different color if it is selected or not
            try
            {
                Pen penUnselected = new Pen(_oFillColorEnd, _fThickness),
                    penSelected = new Pen(_oSelectedColor, _fSelectedThickness),
                    penDoubleSelected = new Pen(_oDoubleSelected, _fSelectedThickness),
                    penWhite = new Pen(Program.Model.Background.StartColor, _fThickness * 2);

                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    _CurveSegments[i].ArrowSize = _fThickness * 5;
                }

                Draw2(g, penWhite, penWhite, penWhite, false, false);

                Draw2(g, penUnselected, penSelected, penDoubleSelected, true, true);

                DrawDecorator(g, (IsSelected ? penSelected : penUnselected ) , penWhite, _fThickness);
                if (ShowText && (!NullItem) && (Item is Edge) && ((Edge)Item).IsReaction)
                    g.DrawString(Name, _oLabelFont, new SolidBrush(_oLabelColor), MiddleRegion.X, MiddleRegion.Y - 10);
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Some Decorators use text within the process marker (i.e: Unspecified or Unknown Process). This method
        /// renders that text. 
        /// </summary>
        /// <param name="g">Graphics Device to draw upon</param>
        /// <param name="sLabel">Label to render</param>
        /// <param name="oRect">Rectangle in which to render</param>
        /// <param name="oMiddle">The Middle of that rectangle</param>
        /// <param name="oPen">The Foreground Pen</param>
        /// <param name="oBrush">The Foreground Brush</param>
        /// <param name="oBackground">The Background Brush</param>
        private static void RenderDecoratorString(Graphics g, string sLabel, RectangleF oRect, PointF oMiddle, Pen oPen, Brush oBrush, Brush oBackground)
        {
            Font oFont = new Font(SystemFonts.DefaultFont, FontStyle.Bold);
            g.FillRectangle(oBackground, oRect);
            g.DrawRectangle(oPen, oRect.X, oRect.Y, oRect.Width, oRect.Height);
            SizeF oSize = g.MeasureString(sLabel, oFont);
            PointF oPoint = new PointF(oMiddle.X - oSize.Width / 2f, oMiddle.Y - oSize.Height / 2f + oPen.Width/2f);
            g.DrawString(sLabel, oFont, oBrush, oPoint);
        }

        /// <summary>
        /// Draws the MiddleDecorator if set. 
        /// </summary>
        /// <param name="g">Graphics device to draw upon</param>
        /// <param name="oPen">the pen to be used when drawing the decorators</param>
        /// <param name="penWhite">the white pen to be used for background</param>
        /// <param name="fThickness">the thickness to be used</param>
        private void DrawDecorator(Graphics g, Pen oPen, Pen penWhite, float fThickness)
        {
            if (MiddleDecorator == ProcessDecorator.None) return;

            float fMin = Math.Min(MiddleRegion.Width, MiddleRegion.Height);

            RectangleF oRect = new RectangleF( MiddleRegion.X + MiddleRegion.Width/2f - fMin/2f, MiddleRegion.Y + MiddleRegion.Height/2f - fMin/2f,
                fMin, fMin);
            Brush oBrush = new SolidBrush(oPen.Color);
            Brush oBackground = new SolidBrush(penWhite.Color);
            PointF oMiddle = new PointF(MiddleRegion.X + MiddleRegion.Width / 2f, MiddleRegion.Y + MiddleRegion.Height / 2f);
            switch (MiddleDecorator)
            {


                case ProcessDecorator.Empty:
                    g.FillRectangle(oBackground, oRect);
                    g.DrawRectangle(oPen, oRect.X, oRect.Y, oRect.Width, oRect.Height);
                    break;
                case ProcessDecorator.Omitted:
                    RenderDecoratorString(g, "//", oRect, oMiddle, oPen, oBrush, oBackground);
                    break;
                case ProcessDecorator.Uncertain:
                    RenderDecoratorString(g, "?", oRect, oMiddle, oPen, oBrush, oBackground); 
                    break;
                case ProcessDecorator.Association:
                    g.FillEllipse(oBrush, oRect);
                    break;
                case ProcessDecorator.Dissociation:
                    g.FillEllipse(oBackground, oRect);
                    g.DrawEllipse(oPen, oRect);
                    oRect.Inflate(-fThickness * 1.5f, -fThickness * 1.5f);
                    g.DrawEllipse(oPen, oRect);
                    break;
                default:
                case ProcessDecorator.None:
                    break;
            }
        }

        /// <summary>
        /// Draws the entire set of curves with the middle segment 
        /// </summary>
        /// <param name="g">graphics to draw on</param>
        public void Draw2(Graphics g, Pen penUnselected, Pen penSelected, Pen penDoubleSelected, bool drawArrow, bool useCurveColors)
        {
            if (_CurveSegments == null || _CurveSegments.Count < 2 || _MiddleSegment == null) return;

            //penSelected.DashStyle = DashStyle.Dash;
            //penDoubleSelected.Width += 0F;// 1.5F;
            penUnselected.LineJoin = penSelected.LineJoin = penDoubleSelected.LineJoin = LineJoin.Round;
            penUnselected.DashStyle = penSelected.DashStyle = penDoubleSelected.DashStyle = _DashStyle;

            //draw bezier and circles around the four control points            
            foreach (CurveSegment c in _CurveSegments)
            {
                //call the draw function of the Curve class - first pen for the main curve, second pen for the control point

                if (c.IsSelected && IsSelected)
                    c.Draw(g, penSelected, penDoubleSelected, _IsSelected, drawArrow);
                else
                    if (IsSelected)
                        c.Draw(g, penSelected, penSelected, _IsSelected, drawArrow);
                    else
                    {
                        if (useCurveColors)
                            penUnselected.Color = c.Color;
                        c.Draw(g, penUnselected, penUnselected, IsSelected, drawArrow);
                    }
            }

            if (IsSelected)
            {
                if (_showMiddleSegment)
                {
                    g.DrawLine(penSelected, _MiddleSegment[0].Point, _MiddleSegment[1].Point);  //the middle line
                    g.FillEllipse(penSelected.Brush, _MiddleSegment[0].X - 1, _MiddleSegment[0].Y - 1, 2, 2);
                }
                g.FillEllipse(penSelected.Brush, _MiddleSegment[1].X - 1, _MiddleSegment[1].Y - 1, 2, 2);
            }
            else
            {
                if (_showMiddleSegment)
                {
                    g.DrawLine(penUnselected, _MiddleSegment[0].Point, _MiddleSegment[1].Point);
                    g.FillEllipse(penUnselected.Brush, _MiddleSegment[0].X - 1, _MiddleSegment[0].Y - 1, 2, 2);
                }
                g.FillEllipse(penUnselected.Brush, _MiddleSegment[1].X - 1, _MiddleSegment[1].Y - 1, 2, 2);
            }            

            if (IsSelected && !AllStraight)
            {
                //draw circles on the mid-segment
                for (int i = 0; i < 2; ++i)
                    g.DrawEllipse(penSelected, _MiddleSegment[i].X - 3, _MiddleSegment[i].Y - 3, 6, 6);

                //control points on the mid segment
                DrawLineAndCircle(g, penSelected, _MiddleSegment[2].Point, _MiddleSegment[0].Point);
                DrawLineAndCircle(g, penSelected, _MiddleSegment[3].Point, _MiddleSegment[1].Point);
            }

        }

        #endregion

        /// <summary>
        /// Returns a list of the items that are connected as inputs
        /// </summary>
        /// <returns></returns>
        public override List<IDrawable> Reactants
        {
            get
            {
                List<IDrawable> inputs = new List<IDrawable>();
                if (_CurveSegments == null || _CurveSegments.Count < 1) return inputs;
                for (int i = 0; i < _numInputs; ++i)
                {
                    inputs.Add(_CurveSegments[i].StartNode);
                }
                return inputs;
            }
            set
            {
                if (_CurveSegments == null) return;
                List<IDrawable> inputs = new List<IDrawable>();
                for (int i = 0; i < _numInputs; ++i)
                {
                    inputs.Add(_CurveSegments[i].StartNode);
                }
                foreach (IDrawable id in value)
                {
                    if (!(inputs.Contains(id))) AddReactant(id);
                }
                foreach (IDrawable id in inputs)
                {
                    if (!(value.Contains(id))) RemoveNode(id);
                }
            }
        }
        /// <summary>
        /// Returns a list of items that are connected as outputs
        /// </summary>
        /// <returns></returns>
        public override List<IDrawable> Products
        {
            get
            {
                List<IDrawable> outputs = new List<IDrawable>();
                if (_CurveSegments == null || _CurveSegments.Count < 1) return outputs;
                for (int i = _numInputs; i < _CurveSegments.Count; ++i)
                {
                    outputs.Add(_CurveSegments[i].StartNode);
                }
                return outputs;
            }
            set
            {
                List<IDrawable> outputs = new List<IDrawable>();
                if (_CurveSegments == null) return;
                for (int i = _numInputs; i < _CurveSegments.Count; ++i)
                {
                    outputs.Add(_CurveSegments[i].StartNode);
                }
                
                foreach (IDrawable id in value)
                {
                    if (!(outputs.Contains(id))) AddReactant(id);
                }
                foreach (IDrawable id in outputs)
                {
                    if (!(value.Contains(id))) RemoveNode(id);
                }
            }
        }

        /// <summary>
        /// Returns a list of items that are connected as input and output
        /// </summary>
        /// <returns></returns>
        public override List<IDrawable> Nodes
        {
            get
            {
                List<IDrawable> idrawables = new List<IDrawable>();
                if (_CurveSegments == null || _CurveSegments.Count < 1) return idrawables;
                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    idrawables.Add(_CurveSegments[i].StartNode);
                }
                return idrawables;
            }
        }

        /// <summary>
        /// This is used to inform the connector that a node has been removed
        /// </summary>
        /// <param name="ig"></param>
        public override bool RemoveNode(IDrawable id)
        {
            if (FixedReaction) return false;
            List<IDrawable> outputs = Products;
            for (int i = 0; i < _CurveSegments.Count; ++i)
            {
                if (_CurveSegments[i].StartNode == id)
                {
                    _CurveSegments[i].IsSelected = true;
                    foreach (IDrawable id2 in outputs)  //for each output item
                    {
                        if (id != id2 && id.Item is Node && id2.Item != null)  
                        {
                            if (id2.Item is Edge)     //it is an edge or part (pops)
                            {
                                Edge e = (Edge)id2.Item;
                                e.RemoveModifier((Node)id.Item);
                            }
                            else
                                if (id2.Item is Part)
                                {
                                    Edge e = ((Part)id2.Item).PoPS;
                                    e.RemoveModifier((Node)id.Item);
                                }
                        }
                    }
                }
                else
                    _CurveSegments[i].IsSelected = false;  //make sure that extra segments are not selected
            }
            return RemoveSelectedCurves(false);
        }

        /// <summary>
        /// Change the selected curves into modifier curves
        /// </summary>
        public virtual void ChangeToModifiers()
        {
            foreach (CurveSegment curve in _CurveSegments)
            {
                if (curve.IsSelected)
                {
                    curve.IsModifier = true;
                    curve.Segments[curve.Segments.Count - 1].End = new ControlPoint(curve.Segments[curve.Segments.Count - 1].End);
                    curve.Segments[curve.Segments.Count - 1].ControlPoint2 = new ControlPoint(curve.Segments[curve.Segments.Count - 1].ControlPoint2);
                }
            }
        }

        /// <summary>
        /// Set the start color for selected curves
        /// if end color and start color are the same, then no gradient
        /// is used
        /// </summary>
        public override Color ColorStart
        {
            get
            {
                return _oFillColorStart;
            }
            set
            {
                _oFillColorStart = value;
                bool noneSelected = true;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsSelected)
                    {
                        c.Color = value;
                        noneSelected = false;
                    }
                }
                if (noneSelected)
                {
                    foreach (CurveSegment c in _CurveSegments)
                    {
                        c.Color = value;
                    }
                }
            }
        }

        /// <summary>
        /// Set the end color for selected curves
        /// if end color and start color are the same, then no gradient
        /// is used
        /// </summary>
        public override Color ColorEnd
        {
            get
            {
                return _oFillColorEnd;
            }
            set
            {
                _oFillColorEnd = value;
                bool noneSelected = true;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsSelected)
                    {
                        c.Color = value;
                        noneSelected = false;
                    }
                }
                if (noneSelected)
                {
                    foreach (CurveSegment c in _CurveSegments)
                    {
                        c.Color = value;
                    }
                }
            }
        }

        /// <summary>
        /// Change the selected curves into reactant curves
        /// </summary>
        public virtual void ChangeToReactants()
        {
            for (int i=0; i < _CurveSegments.Count; ++i)
            {
                CurveSegment curve = _CurveSegments[i];
                if (curve.IsSelected)
                {
                    curve.IsModifier = false;
                    curve.IsReactant = true;
                    
                    curve.Segments[curve.Segments.Count - 1].End = (curve.Segments[curve.Segments.Count - 1].End);
                    curve.Segments[curve.Segments.Count - 1].ControlPoint2 = (curve.Segments[curve.Segments.Count - 1].ControlPoint2);

                    if (i >= _numInputs)
                    {
                        _CurveSegments.RemoveAt(i);
                        _CurveSegments.Insert(0, curve);
                    }
                }
            }
        }

        /// <summary>
        /// replaces all instances of one node with another in this reaction
        /// </summary>
        /// <param name="NodeToReplace"></param>
        /// <param name="ReplaceWith"></param>
        public override void SubstituteNode(IDrawable NodeToReplace, IDrawable ReplaceWith) 
        {
            if (FixedReaction || NodeToReplace == null || ReplaceWith == null) return;

            try
            {
                bool input = false, output = false;
                for (int i = 0; i < _CurveSegments.Count; ++i)
                {
                    if (_CurveSegments[i].StartNode == NodeToReplace || _CurveSegments[i].StartNode == NodeToReplace.Original)
                    {
                        _CurveSegments[i].StartNode = ReplaceWith;
                        if (i < _numInputs)
                            input = true;
                        else
                            output = true;
                    }
                }
                if (input || output)
                {
                    if (NodeToReplace is IGlyph)
                    {
                        (NodeToReplace as IGlyph).RemoveConnector(this);
                    }
                    if (ReplaceWith is IGlyph)
                    {
                        if (input)
                            (ReplaceWith as IGlyph).AddOutgoingConnection(this);
                        else
                            (ReplaceWith as IGlyph).AddIncomingConnection(this);
                    }
                    if (NodeToReplace.Item is Node && ReplaceWith.Item is Node && _Item != null)
                        (_Item as Edge).SubstituteNode(NodeToReplace.Item as Node, ReplaceWith.Item as Node);
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Returns the arrow head type for the selected segment (first one if multiple selected)
        /// Sets the arrow head type for all selected segments
        /// </summary>
        [CategoryAttribute("Arrow Head"), Description("The type of arrow head of this curve")]
        public override Connector.ArrowHeadType ArrowHead
        {
            get
            {
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsSelected)
                        return c.ArrowHead;
                }
                return ArrowHeadType.Arrow;
            }
            set
            {
                bool noneselected = true;

                if (Item is Edge && ((Edge)Item).IsRegulation)
                {
                    if (value == ArrowHeadType.Arrow) value = ArrowHeadType.Bar;

                    if (value == ArrowHeadType.Bar)
                    {
                        ((Edge)Item)._ReactionType = "-";
                    }
                    else
                    {
                        ((Edge)Item)._ReactionType = "+";
                    }
                }

                int i = 0;
                foreach (CurveSegment c in _CurveSegments)
                {
                    if (c.IsSelected && i >= _numInputs)
                    {
                        c.ArrowHead = value;
                        noneselected = false;
                    }
                    ++i;
                }
                if (noneselected)
                {
                    foreach (CurveSegment c in _CurveSegments)
                    {
                        c.ArrowHead = value;
                    }
                }
            }
        }

        protected int _numInputs;

        /// <summary>
        /// get a list of all the modifers -- these are usually other connectors
        /// </summary>
        public override List<IDrawable> Modifiers
        {
            get 
            {
                List<IDrawable> list = new List<IDrawable>();
                foreach (CurveSegment curve in _CurveSegments)
                {
                    if (curve.IsModifier && curve.StartNode != null && !list.Contains(curve.StartNode))
                        list.Add(curve.StartNode);
                }
                return list;
            }
            set
            {
                List<IDrawable> nodes = new List<IDrawable>();
                List<IDrawable> modifiers = Modifiers;

                foreach (CurveSegment curve in _CurveSegments)
                {
                    curve.IsSelected = (modifiers.Contains(curve.StartNode) || curve.IsModifier);
                    if (value != null && value.Contains(curve.StartNode))
                    {
                        curve.IsSelected = false;
                    }
                }

                RemoveSelectedCurves();

                if (value != null)
                {
                    foreach (IDrawable id in value)
                    {
                        if (FindCurveSegments(id).Count == 0)
                        {
                            AddModifier(id);
                        }
                    }
                }

                foreach (IDrawable id in value)
                {
                    if (id.Item is Node && !id.NullItem)
                    {
                        ((Edge)Item).AddModifier((Node)id.Item);
                    }
                }
            }
        }

        [CategoryAttribute("Inputs and Outputs"), Description("The number of inputs in this reaction")]
        public override int ReactantCount
        {
            get { return _numInputs; }
        }
        
        [CategoryAttribute("Inputs and Outputs"), Description("The number of outputs in this reaction")]
        public override int ProductCount
        {
            get { return _CurveSegments.Count - _numInputs; }
        }

        public override Item Item
        {
            get
            {
                if (_CurveSegments != null && _CurveSegments.Count > _numInputs)
                {
                    if (_CurveSegments[_numInputs].StartNode != null && 
                        _CurveSegments[_numInputs].StartNode.Item is Edge)
                        return _CurveSegments[_numInputs].StartNode.Item;

                    //if (_CurveSegments[_numInputs].StartNode.Item is Part)
                    //    return ((Part)_CurveSegments[_numInputs].StartNode.Item).PoPS;
                }
                return base.Item;
            }
            set
            {
                base.Item = value;
                if ((Item is Edge) && ((Edge)Item).IsRegulation)
                    if (((Edge)Item)._ReactionType.Equals("+"))
                    {
                        ArrowHead = ArrowHeadType.Circle;
                    }
                    else
                    {
                        ArrowHead = ArrowHeadType.Bar;
                    }
            }
        }


        bool IsUniqueId(SBMLExtension.LayoutExtension.Layout oLayout, string sId)
        {
            foreach (SBMLExtension.LayoutExtension.ReactionGlyph glyph in oLayout.ReactionGlyphs)
            {
                if (!IsUniqueId(glyph, sId)) return false;
            }
            return true;
        }

        bool IsUniqueId(SBMLExtension.LayoutExtension.ReactionGlyph glyph, string sId)
        {
            bool bResult = true;
            foreach (SBMLExtension.LayoutExtension.SpeciesReference oRef in glyph.SpeciesReferences)
            {
                if (oRef.ID == sId)
                    return false;
            }
            return bResult;
        }


        public void FillCurveSegments(SBMLExtension.LayoutExtension.ReactionGlyph oGlyph, SBMLExtension.LayoutExtension.Layout oLayout)
        {



            if (_MiddleSegment[0].Point != _MiddleSegment[1].Point)
            {
                oGlyph.Curve.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                    new SBMLExtension.LayoutExtension.Point(_MiddleSegment[0].Point),
                    new SBMLExtension.LayoutExtension.Point(_MiddleSegment[1].Point)
                    ));
            }



            int nCount = 0;
            foreach (CurveSegment curve in _CurveSegments) //for each curve segment
            {

                SBMLExtension.LayoutExtension.SpeciesReference oRef = new SBMLExtension.LayoutExtension.SpeciesReference();
                System.Collections.ObjectModel.Collection<SBMLExtension.LayoutExtension.CurveSegment> curveSegments = new System.Collections.ObjectModel.Collection<SBMLExtension.LayoutExtension.CurveSegment>();

                IDrawable startNode = curve.StartNode;
                if (startNode == null) continue;
                oRef.Name = startNode.GlobalName;

                string sId = oRef.Name + "_sr_" + nCount.ToString();
                int nAltCount = 0;
                while (!(IsUniqueId(oLayout, sId)))
                {
                    sId = oRef.Name + "_sr_" + nCount.ToString() + "_" + nAltCount.ToString();
                    nAltCount++;
                }

                oRef.ID = sId;

                if (startNode is Connector)
                {
                    oRef.Role = "modifier";
                    oRef.ObjectRole = "modifier";
                }
                else
                {
                    if (nCount < _numInputs)
                    {
                        oRef.Role = "substrate";
                        oRef.ObjectRole = "substrate";
                    }
                    else
                    {
                        oRef.Role = "product";
                        oRef.ObjectRole = "product";
                    }
                }

                switch (curve.ArrowHead)
                {
                    case ArrowHeadType.Arrow:
                        //oRef.Role = "product";
                        //oRef.ObjectRole = "product";
                        break;

                    case ArrowHeadType.Circle:
                    case ArrowHeadType.Catalysis:
                        oRef.Role = "activation";
                        oRef.ObjectRole = "activation";
                        break;

                    case ArrowHeadType.Inhibition:
                    case ArrowHeadType.Bar:
                        oRef.Role = "inhibition";
                        oRef.ObjectRole = "inhibition";
                        break;
                    case ArrowHeadType.Modulation:
                        oRef.Role = "modulation";
                        oRef.ObjectRole = "modulation";
                        break;
                    case ArrowHeadType.Stimulation:
                        oRef.Role = "stimulation";
                        oRef.ObjectRole = "stimulation";
                        break;
                    case ArrowHeadType.Trigger:
                        oRef.Role = "trigger";
                        oRef.ObjectRole = "trigger";
                        break;
                    case ArrowHeadType.None:
                    default:
                        //oRef.Role = "substrate";
                        //oRef.ObjectRole = "substrate";
                        break;
                }

                SBMLExtension.LayoutExtension.SpeciesGlyph speciesGlyph = oLayout.findSpeciesGlyphByReference(oRef.Name);
                if (speciesGlyph != null)
                    oRef.SpeciesGlyph = speciesGlyph.ID;

                oRef.ObjectRole = oRef.Role;

                for (int i = 0; i < curve.Count; ++i)  //for each sub-segment inside each curve
                {
                    PointF start;
                    if (i > 0)
                        start = curve.Segments[i].Start.Point;  //start[i] = end[i-1] except for 1st sub-segment
                    else
                        start = curve.StartPoint;

                    if (curve.IsStraight)
                    {
                        if (oRef.Role == "product")
                        {
                            curveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                             new SBMLExtension.LayoutExtension.Point(curve.Segments[i].End.Point),
                             new SBMLExtension.LayoutExtension.Point(start)
                             ));
                        }
                        else
                        {
                            curveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(start),
                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].End.Point)
                                ));
                        }
                    }
                    else
                    {
                        if (oRef.Role == "product")
                        {
                            curveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(

                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].End.Point),
                                new SBMLExtension.LayoutExtension.Point(start),

                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].ControlPoint2.Point), // base 2
                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].ControlPoint1.Point) // base 1
                                ));
                        }
                        else
                        {
                            curveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(start),
                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].End.Point),
                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].ControlPoint1.Point), // base 1
                                new SBMLExtension.LayoutExtension.Point(curve.Segments[i].ControlPoint2.Point) // base 2
                                ));
                        }
                    }
                }
                oRef.Curve.CurveSegments = curveSegments;
                oGlyph.SpeciesReferences.Add(oRef);
                nCount++;
            }
        }


        public override SBMLExtension.EmlRenderExtension.LocalStyle ToStyle(SBMLExtension.EmlRenderExtension.RenderInformation oInfo)
        {

            SBMLExtension.EmlRenderExtension.LocalStyle oResult = new SBMLExtension.EmlRenderExtension.LocalStyle();
            SBMLExtension.EmlRenderExtension.Group oGroup = new SBMLExtension.EmlRenderExtension.Group();
            oGroup.Stroke = oInfo.AddColorDefinition(_oFillColorEnd);
            oGroup.StrokeWidth = _fThickness.ToString();
            oResult.Group = oGroup;

            return oResult;
        }
        

        public SBMLExtension.LayoutExtension.ReactionGlyph ToGlyph(SBMLExtension.LayoutExtension.Layout oLayout)
        {
            SBMLExtension.LayoutExtension.ReactionGlyph oGlyph = new SBMLExtension.LayoutExtension.ReactionGlyph(
                "glyph_" + Name, Name,
                new double[] { Location.X, Location.Y, 0f },
                new double[] { Dimensions.Width, Dimensions.Height, 0f }
                );

            FillCurveSegments(oGlyph, oLayout);


            return oGlyph;
        }

        /// <summary>
        /// Replace each node with its alias if the alias is closer
        /// </summary>
        public override void ReplaceNodesWithNearestAlias()
        {
            List<IDrawable> inputs = Reactants, outputs = Products;
            IDrawable temp;
            foreach (IDrawable drawable in inputs)
            {
                if (drawable.Item != null)
                {
                    temp = drawable.Item.Drawable;
                    if (temp != null && temp.Alias != null && temp.Alias.Count > 0)
                    {
                        List<IDrawable> list = new List<IDrawable>();
                        foreach (IDrawable id in temp.Alias)
                            if (id.Parent == drawable.Parent)
                                list.Add(id);
                        if (list.Count > 0)
                        {
                            IDrawable closest = Utility.FindClosestDrawable(Utility.CenterPoint(outputs), list);
                            SubstituteNode(drawable, closest);
                        }
                    }
                }
            }
            foreach (IDrawable drawable in outputs)
            {
                if (drawable.Item != null)
                {
                    temp = drawable.Item.Drawable;
                    if (temp != null && temp.Alias != null && temp.Alias.Count > 0)
                    {
                        List<IDrawable> list = new List<IDrawable>();
                        foreach (IDrawable id in temp.Alias)
                            if (id.Parent == drawable.Parent)
                                list.Add(id);
                        if (list.Count > 0)
                        {
                            IDrawable closest = Utility.FindClosestDrawable(Utility.CenterPoint(inputs), list);
                            SubstituteNode(drawable, closest);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks the rate expression and automatically makes all the modifier connectors
        /// </summary>
        public override void GenerateModifiers()
        {
            Edge edge = _Item as Edge;
            if (edge == null || !edge.IsReaction) return;

            List<IDrawable> extra = new List<IDrawable>();
            List<IDrawable> modifiers = Modifiers;

            if (modifiers != null)
                foreach (IDrawable id in modifiers) 
                    extra.Add(id);

            foreach (Node n in edge.Modifiers)
            {
                if (n is Species && n.Drawable != null && !n.Drawable.NullItem)
                {
                    //BezierConnector bc = new BezierConnector(n.Drawable, this, false);
                    //bc.ArrowHead = ArrowHeadType.Circle;
                    //Program.Model.AddToModel(bc);
                    if (modifiers.Contains(n.Drawable))
                    {
                        extra.Remove(n.Drawable);
                    }
                    else
                    {
                        AddModifier(n.Drawable);
                    }
                }
            }
            foreach (IDrawable id in extra) 
                RemoveNode(id);

            foreach (CurveSegment c in _CurveSegments)
            {
                if (c.IsModifier && c.StartNode != null && !string.IsNullOrEmpty(Rate))
                    if (Edge.RateChangeWithRespectTo(Rate, c.StartNode.Name) < 0)
                        c.ArrowHead = ArrowHeadType.Bar;
                    else
                        c.ArrowHead = ArrowHeadType.Circle;
            }

            Utility.Refresh();
        }

        /// <summary>
        /// write information about this drawable to an XML file
        /// </summary>
        /// <param name="reader"></param>
        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);

            writer.WriteStartElement("BezierConnector", "");

            writer.WriteStartElement("LinkControlPoints", "");
            writer.WriteValue(_linkControlPoints);
            writer.WriteEndElement();

            writer.WriteStartElement("ShowMiddleSegment", "");
            writer.WriteValue(_showMiddleSegment);
            writer.WriteEndElement();

            writer.WriteStartElement("SelectedColor", "");
            writer.WriteValue(_oSelectedColor.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("DoubleSelected", "");
            writer.WriteValue(_oDoubleSelected.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("SelectedThickness", "");
            writer.WriteValue(_fSelectedThickness);
            writer.WriteEndElement();

            writer.WriteStartElement("DashType", "");
            writer.WriteValue((int)(_DashStyle - DashStyle.Custom));
            writer.WriteEndElement();


            if (_CurveSegments.Count > 0)
            {
                Hashtable AllItemsWithPrefix = null;
                if (Item != null && Item.Root is Module)
                {
                    AllItemsWithPrefix = ((Module)Item.Root).AllItemsWithPrefix;
                }
                writer.WriteStartElement("Curves", "");
                foreach (CurveSegment curve in _CurveSegments)
                {
                    curve.WriteXML(writer, Item, AllItemsWithPrefix);
                }
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }

        /// <summary>
        /// read information about this drawable from an XML file
        /// </summary>
        /// <param name="reader"></param>
        public override bool ReadXML(XmlReader reader)
        {
            base.ReadXML(reader);

            bool read = reader.Read();

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "BezierConnector")
            {
                //Edge edge = _Item as Edge;
                //if (edge == null) return false;

                bool valid = true;
                _CurveSegments.Clear();
                _numInputs = 0;

                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "LinkControlPoints":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _linkControlPoints = reader.ReadContentAsBoolean();
                                
                                break;
                            case "ShowMiddleSegment":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _showMiddleSegment = reader.ReadContentAsBoolean();
                                
                                break;
                            case "SelectedColor":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oSelectedColor = Color.FromName(reader.ReadContentAsString());
                                
                                break;
                            case "DoubleSelected":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oDoubleSelected = Color.FromName(reader.ReadContentAsString());
                                
                                break;
                            case "SelectedThickness":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _fSelectedThickness = reader.ReadContentAsFloat();
                                
                                break;
                            case "DashType":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)                                
                                    _DashStyle = DashStyle.Custom + reader.ReadContentAsInt();                                
                                break;
                            case "Curves":
                                {
                                    Hashtable AllItemsWithPrefix = null;
                                    Module root = null;
                                    if (Item != null && Item.Root is Module)
                                    {
                                        root = (Module)Item.Root;
                                        AllItemsWithPrefix = root.AllItemsWithPrefix;
                                    }
                                    while (valid)
                                    {
                                        CurveSegment curve = new CurveSegment();
                                        valid = curve.ReadXML(reader, this, root, AllItemsWithPrefix);
                                        if (valid)
                                        {
                                            //if (curve.StartNode.NullItem)
                                            //    curve.StartNode.Parent = Parent;

                                            if (curve.IsModifier || curve.IsReactant)
                                            {
                                                _CurveSegments.Insert(0, curve);
                                                ++_numInputs;
                                            }
                                            else
                                                _CurveSegments.Add(curve);
                                        }
                                    }
                                    FixMiddleSegment();
                                    ColorEnd = ColorEnd;
                                    //DoneModifying(Corner.None,false);
                                }
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }
                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "BezierConnector")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }

                return true;
            }
            return false;
        }

        /// <summary>
        /// sets the tool tip that describes this drawable
        /// </summary>
        /// <param name="tooltip"></param>
        public override void ShowToolTip(ToolTip tooltip, Control pnlCanvas, PointF location)
        {
            tooltip.ToolTipTitle = "Reaction:";
            tooltip.SetToolTip(pnlCanvas,
                String.Format(
                            "{1}{2}{1}"
                          + "{1}Rate :\t{0}",                
                Rate,
                "\r\n",
                Reaction));
        }

        public override void ShowCornerToolTip(ToolTip tooltip, Control pnlcanvas, PointF location, Corner corner)
        {
            tooltip.SetToolTip(pnlcanvas, "move control point");
        }

        public static BezierConnector DEFAULT = null;
    }
}
